General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/liver_regen"

Load libraries

library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] FALSE
use_python(Sys.which("python"))
py_config()
python:         /home/tpires/bin/miniconda3/bin/python
libpython:      /home/tpires/bin/miniconda3/lib/libpython3.8.so
pythonhome:     /home/tpires/bin/miniconda3:/home/tpires/bin/miniconda3
version:        3.8.3 (default, May 19 2020, 18:47:26)  [GCC 7.3.0]
numpy:          /home/tpires/bin/miniconda3/lib/python3.8/site-packages/numpy
numpy_version:  1.22.1

NOTE: Python version was forced by RETICULATE_PYTHON

Load data (from all cells)

library(Seurat)
Attaching SeuratObject
library(ggplot2)
library(ggrepel)
library(destiny)
library(plyr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:plyr’:

    arrange, count, desc, failwith, id, mutate, rename, summarise, summarize

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(RColorBrewer)

Other functions

# Plot correlations
plotCorr = function(cort, sp1, sp2){
  # cluster and order labels
  hcr = hclust(dist(cort$r), method = "ward.D2")
  hcc = hclust(dist(t(cort$r)), method = "ward.D2")
  hcr = hcr$labels[hcr$order]
  hcc = hcc$labels[hcc$order]

  # reshaping the correlations
  plot_df = reshape2::melt(cort$r)
  plot_df$Var1 = factor(plot_df$Var1, levels = rev(hcr))
  plot_df$Var2 = factor(plot_df$Var2, levels = hcc)

  # add pvalue and max cor infor
  plot_df$padj = -log10(reshape2::melt(cort$p.adj+min(cort$p.adj[cort$p.adj>0])/10)$value)
  plot_df$rowmax = apply(Reduce(cbind, lapply(names(cort$maxrow),
                                              function(n) plot_df$Var1==n &
                                                plot_df$Var2==colnames(cort$r)[cort$maxrow[n]])),
                         1, any)
  plot_df$colmax = apply(Reduce(cbind, lapply(names(cort$maxcol),
                                              function(n) plot_df$Var2==n &
                                                plot_df$Var1==rownames(cort$r)[cort$maxcol[n]])),
                         1, any)
  plot_df$markcol = plot_df$value>quantile(plot_df$value, 0.98)

  # getting a colourscale where 0 is white in the middle, and intensity leveled by max(abs(value))
  cols = colorRampPalette(c(rev(RColorBrewer::brewer.pal(9, "Blues")),
                            RColorBrewer::brewer.pal(9, "Reds")))(101)
  br = seq(-max(abs(cort$r)), max(abs(cort$r)), length.out = 101)
  cols = cols[!(br>max(cort$r) | br<min(cort$r))]

  corplot = ggplot()+
    geom_point(data = plot_df, mapping = aes(x = Var2, y = Var1, fill = value, size = padj),
               shape = 21)+
    geom_point(data = plot_df[plot_df$rowmax,], mapping = aes(x = Var2, y = Var1, size = padj),
               shape = "—", show.legend = F, colour = "grey10")+
    geom_point(data = plot_df[plot_df$colmax,], mapping = aes(x = Var2, y = Var1, size = padj),
               shape = "|", show.legend = F, colour = "grey10")+
    scale_x_discrete(expand = c(0,0.7))+
    scale_y_discrete(expand = c(0,0.7))+
    scale_fill_gradientn(breaks = signif(c(min(cort$r)+0.005, 0, max(cort$r)-0.005),2),
                         values = scales::rescale(c(min(br), 0, max(br))),
                         colours = cols)+
    labs(x = sp2, y = sp1, fill = "Spearman's\nrho", size = "-log10\nadj. p-value")+
    theme_classic()+
    theme(axis.title = element_text(colour = "black", face = "bold"),
          axis.text = element_text(colour = "black"),
          axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          legend.title = element_text(size = 9),
          legend.text = element_text(size = 8))

  return(corplot)
}

Immune cell subsetting

Subset immune cells

# Plot correlations
plotCorr = function(cort, sp1, sp2){
  # cluster and order labels
  hcr = hclust(dist(cort$r), method = "ward.D2")
  hcc = hclust(dist(t(cort$r)), method = "ward.D2")
  hcr = hcr$labels[hcr$order]
  hcc = hcc$labels[hcc$order]

  # reshaping the correlations
  plot_df = reshape2::melt(cort$r)
  plot_df$Var1 = factor(plot_df$Var1, levels = rev(hcr))
  plot_df$Var2 = factor(plot_df$Var2, levels = hcc)

  # add pvalue and max cor infor
  plot_df$padj = -log10(reshape2::melt(cort$p.adj+min(cort$p.adj[cort$p.adj>0])/10)$value)
  plot_df$rowmax = apply(Reduce(cbind, lapply(names(cort$maxrow),
                                              function(n) plot_df$Var1==n &
                                                plot_df$Var2==colnames(cort$r)[cort$maxrow[n]])),
                         1, any)
  plot_df$colmax = apply(Reduce(cbind, lapply(names(cort$maxcol),
                                              function(n) plot_df$Var2==n &
                                                plot_df$Var1==rownames(cort$r)[cort$maxcol[n]])),
                         1, any)
  plot_df$markcol = plot_df$value>quantile(plot_df$value, 0.98)

  # getting a colourscale where 0 is white in the middle, and intensity leveled by max(abs(value))
  cols = colorRampPalette(c(rev(RColorBrewer::brewer.pal(9, "Blues")),
                            RColorBrewer::brewer.pal(9, "Reds")))(101)
  br = seq(-max(abs(cort$r)), max(abs(cort$r)), length.out = 101)
  cols = cols[!(br>max(cort$r) | br<min(cort$r))]

  corplot = ggplot()+
    geom_point(data = plot_df, mapping = aes(x = Var2, y = Var1, fill = value, size = padj),
               shape = 21)+
    geom_point(data = plot_df[plot_df$rowmax,], mapping = aes(x = Var2, y = Var1, size = padj),
               shape = "—", show.legend = F, colour = "grey10")+
    geom_point(data = plot_df[plot_df$colmax,], mapping = aes(x = Var2, y = Var1, size = padj),
               shape = "|", show.legend = F, colour = "grey10")+
    scale_x_discrete(expand = c(0,0.7))+
    scale_y_discrete(expand = c(0,0.7))+
    scale_fill_gradientn(breaks = signif(c(min(cort$r)+0.005, 0, max(cort$r)-0.005),2),
                         values = scales::rescale(c(min(br), 0, max(br))),
                         colours = cols)+
    labs(x = sp2, y = sp1, fill = "Spearman's\nrho", size = "-log10\nadj. p-value")+
    theme_classic()+
    theme(axis.title = element_text(colour = "black", face = "bold"),
          axis.text = element_text(colour = "black"),
          axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          legend.title = element_text(size = 9),
          legend.text = element_text(size = 8))

  return(corplot)
}

Lymphoid cell analysis

Subset Lymphoid cells

immune_pops = c("ab-T cells", "gd-T cells", "Plasmablasts", "B cells")
all_l_cells = allcells_css[,allcells_css@meta.data$allcells_clusters %in% immune_pops]
all_l_cells = suppressWarnings(SCTransform(all_l_cells, do.correct.umi = T, verbose = F, 
                                             vars.to.regress=c("unique_name","nCount_RNA"),
                                             variable.features.rv.th = 1, seed.use = 1,
                                             return.only.var.genes = F, 
                                             variable.features.n = NULL))
all_l_cells = RunPCA(all_l_cells, verbose = F)
all_l_cells = RunUMAP(all_l_cells, dims = 1:25, verbose = F)
DimPlot(all_l_cells, reduction = "umap", group.by = "Condition")
DimPlot(all_l_cells, reduction = "umap", group.by = "allcells_clusters")
DimPlot(all_l_cells, reduction = "umap", group.by = "Donor")
DimPlot(all_l_cells, reduction = "umap", group.by = "Phase")
FeaturePlot(all_l_cells, reduction = "umap", features = c("MKI67", "ALB", "S100A8", "COLEC11",
                                                            "NKG7", "TRGC1", "PTPRC", "EPCAM",
                                                            "CD14", "TRAC", "CD3E", "CD8A"))

Cluster and get markers

all_l_cells = FindNeighbors(all_l_cells, reduction = "pca", dims = 1:25,
                            prune.SNN = 1/5, force.recalc = T, graph.name = "pca25")
all_l_cells = FindClusters(all_l_cells, algorithm = 2, verbose = F, graph.name = "pca25",
                           resolution = seq(0.1, 2, 0.1))
DimPlot(all_l_cells, reduction = "umap", group.by = "pca25_res.0.4", label = T)
DimPlot(all_l_cells, reduction = "umap", group.by = "Donor", label = F)
DimPlot(all_l_cells, reduction = "umap", group.by = "Condition", label = F)

all_l_cells = SetIdent(all_l_cells, value = "pca25_res.0.4")
mk_lcells = FindAllMarkers(all_l_cells, logfc.threshold = 0.2, pseudocount.use = 0.1)
write.csv(mk_lcells[mk_lcells$p_val_adj<=0.05,], 
          file = "results/immune/markers_lymphoid_subpop_all.csv", row.names = T, quote = F)


mk02 = FindMarkers(all_l_cells, ident.1 = "0", ident.2 = "2", 
                   logfc.threshold = 0.2, pseudocount.use = 0.1)

Add annotations

new_l_labs = c("0" = "ab-T cells 1",
               "1" = "NK/gd-T cells",
               "2" = "ab-T cells 2",
               "3" = "Infiltrating NK cells", # PTGDS,CX3CR1 (infilt)
               "4" = "IgA+ Plasma cells",
               "5" = "B cells",
               "6" = "IgG+ Plasma cells",
               "7" = "Dividing NK cells",
               "8" = "ab-T cells (stress)")

all_l_cells$lymphoid_annot = new_l_labs[as.character(all_l_cells$pca25_res.0.4)]

Subset T/NK cells

immune_pops = c("ab-T cells 1", "ab-T cells 2", "NK/gd-T cells", "Infiltrating NK cells")
all_t_cells = all_l_cells[,all_l_cells@meta.data$lymphoid_annot %in% immune_pops]
all_t_cells = suppressWarnings(SCTransform(all_t_cells, do.correct.umi = T, verbose = F, 
                                             vars.to.regress=c("unique_name","nCount_RNA"),
                                             variable.features.rv.th = 1, seed.use = 1,
                                             return.only.var.genes = F, 
                                             variable.features.n = NULL))
all_t_cells = RunPCA(all_t_cells, verbose = F)
all_t_cells = RunUMAP(all_t_cells, dims = 1:25, verbose = F)
DimPlot(all_t_cells, reduction = "umap", group.by = "Condition")
DimPlot(all_t_cells, reduction = "umap", group.by = "allcells_clusters")
DimPlot(all_t_cells, reduction = "umap", group.by = "lymphoid_annot")
DimPlot(all_t_cells, reduction = "umap", group.by = "Donor")
DimPlot(all_t_cells, reduction = "umap", group.by = "Phase")
FeaturePlot(all_t_cells, reduction = "umap", features = c("MKI67", "ALB", "S100A8", "COLEC11",
                                                            "NKG7", "TRGC1", "PTPRC", "EPCAM",
                                                            "CD14", "TRAC", "CD3E", "CD8A"))

Cluster and get markers

all_t_cells = FindNeighbors(all_t_cells, reduction = "pca", dims = 1:25,
                            prune.SNN = 1/5, force.recalc = T, graph.name = "pca25")
all_t_cells = FindClusters(all_t_cells, algorithm = 2, verbose = F, graph.name = "pca25",
                           resolution = seq(0.1, 2, 0.1))
DimPlot(all_t_cells, reduction = "umap", group.by = "pca25_res.0.9", label = T)
DimPlot(all_t_cells, reduction = "umap", group.by = "Donor", label = F)
DimPlot(all_t_cells, reduction = "umap", group.by = "Condition", label = F)
DimPlot(all_t_cells, reduction = "umap", group.by = "t_annot", label = T)

all_t_cells = SetIdent(all_t_cells, value = "pca25_res.0.9")
mk_tcells = FindAllMarkers(all_t_cells, logfc.threshold = 0.2, pseudocount.use = 0.1)
write.csv(mk_tcells[mk_tcells$p_val_adj<=0.05,], 
          file = "results/immune/markers_t_subpop_all.csv", row.names = T, quote = F)

saveRDS(mk_tcells, file = "./results/immune/clust_markers_t.RDS")

mk97 = FindMarkers(all_t_cells, ident.1 = "9", ident.2 = "7", 
                   logfc.threshold = 0.2, pseudocount.use = 0.1)
mk26 = FindMarkers(all_t_cells, ident.1 = "2", ident.2 = "6",
                   logfc.threshold = 0.2, pseudocount.use = 0.1)
mk813 = FindMarkers(all_t_cells, ident.1 = "8", ident.2 = "13",
                   logfc.threshold = 0.2, pseudocount.use = 0.1)

Annotate

# https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5007630/
new_t_labs = c("0" = "NK cells 1",
               "1" = "TRM cells", # may have CD4 and CD8; ITGA1
               "2" = "MAIT cells 2", # CXCR6, CCR6, CCR5, some RORC (not DE with the ILC3), some ZBTB16
               "3" = "NK cells 2",
               "4" = "NK cells 3",
               "5" = "CD8 ab-T cells 1",
               "6" = "MAIT cells 1", # hard to be sure, but has many hallmarks, even higher CD8A
               "7" = "Infiltrating NK cells",
               "8" = "Naive CD4+ T cells", 
               "9" = "gd-T cells", # CD3+ vs cl7, TRDC/TRG
               "10" = "CD8 ab-T cells 2",
               "11" = "CD8 ab-T cells (stress)",
               "12" = "Treg",
               "13" = "ILC3") # KIT, AHR, RORC, no CD3/CD8/CD4

all_t_cells$t_annot = new_t_labs[as.character(all_t_cells$pca25_res.0.9)]

Myeloid cell analysis

Subset Myeloid cells

immune_pops = c("cDCs 2", "Macrophages", "Kupffer cells", "cDCs 1", "pDCs")
all_m_cells = allcells_css[,allcells_css@meta.data$allcells_clusters %in% immune_pops]
all_m_cells = suppressWarnings(SCTransform(all_m_cells, do.correct.umi = T, verbose = F, 
                                             vars.to.regress=c("unique_name","nCount_RNA"),
                                             variable.features.rv.th = 1, seed.use = 1,
                                             return.only.var.genes = F, 
                                             variable.features.n = NULL))
all_m_cells = RunPCA(all_m_cells, verbose = F)
all_m_cells = RunUMAP(all_m_cells, dims = 1:25, verbose = F)
DimPlot(all_m_cells, reduction = "umap", group.by = "Condition")
DimPlot(all_m_cells, reduction = "umap", group.by = "allcells_clusters")
DimPlot(all_m_cells, reduction = "umap", group.by = "Donor")
DimPlot(all_m_cells, reduction = "umap", group.by = "Phase")
FeaturePlot(all_m_cells, reduction = "umap", features = c("MKI67", "ALB", "S100A8", "COLEC11",
                                                            "NKG7", "TRGC1", "PTPRC", "EPCAM",
                                                            "CD14", "TRAC", "CD3E", "CD8A"))

Clustering and markers

all_m_cells = FindNeighbors(all_m_cells, reduction = "pca", dims = 1:25,
                            prune.SNN = 1/5, force.recalc = T, graph.name = "pca25")
all_m_cells = FindClusters(all_m_cells, algorithm = 2, verbose = F, graph.name = "pca25",
                           resolution = seq(0.1, 2, 0.1))
DimPlot(all_m_cells, reduction = "umap", group.by = "pca25_res.0.6", label = T)
DimPlot(all_m_cells, reduction = "umap", group.by = "Donor", label = F)
DimPlot(all_m_cells, reduction = "umap", group.by = "Condition", label = F)
DimPlot(all_m_cells, reduction = "umap", group.by = "allcells_clusters", label = F)

all_m_cells = SetIdent(all_m_cells, value = "pca25_res.0.6")
mk_mcells = FindAllMarkers(all_m_cells, logfc.threshold = 0.2, pseudocount.use = 0.1)
write.csv(mk_mcells[mk_mcells$p_val_adj<=0.05,], 
          file = "results/immune/markers_m_subpop_all.csv", row.names = T, quote = F)

saveRDS(mk_mcells, file = "./results/immune/clust_markers_myeloid.RDS")

mk1011 = FindMarkers(all_m_cells, ident.1 = "10", ident.2 = "11",
                     logfc.threshold = 0.2, pseudocount.use = 0.1)

Add annotations

mrks_q = SoupX::quickMarkers(all_m_cells@assays$SCT@counts,
                             all_m_cells@active.ident, N = 10)
View(mrks_q[mrks_q$qval<=0.05,])

new_m_labs = c("0" = "Kupffer cells",
               "1" = "Monocytes/cDCs",
               "2" = "Macrophages",
               "3" = "Monocytes/cDCs",
               "4" = "Kupffer cells",
               "5" = "Monocytes/cDCs",
               "6" = "Monocytes/cDCs",
               "7" = "Macrophages",
               "8" = "Monocytes/cDCs",
               "9" = "cDC1",
               "10" = "pDCs",
               "11" = "pDCs",
               "12" = "Dividing cDCs",
               "13" = "Kupffer cells",
               "14" = "Hepatocytes")

all_m_cells$mye_annot = new_m_labs[as.character(all_m_cells$pca25_res.0.6)]

Subset Monocytes

immune_pops = c("2", "7", "1", "6", "8", "3", "5", "4", "13", "0", "9")
all_mon_cells = all_m_cells[,all_m_cells@meta.data$pca25_res.0.6 %in% immune_pops &
                            all_m_cells@meta.data$allcells_clusters %in% c("Macrophages", "cDCs 1",
                                                                           "cDCs 2", "Kupffer cells")]
all_mon_cells = suppressWarnings(SCTransform(all_mon_cells, do.correct.umi = T, verbose = F, 
                                             vars.to.regress=c("unique_name","nCount_RNA"),
                                             variable.features.rv.th = 1, seed.use = 1,
                                             return.only.var.genes = F, 
                                             variable.features.n = NULL))
all_mon_cells = RunPCA(all_mon_cells, verbose = F)
all_mon_cells = RunUMAP(all_mon_cells, dims = 1:25, verbose = F)
DimPlot(all_mon_cells, reduction = "umap", group.by = "Condition")
DimPlot(all_mon_cells, reduction = "umap", group.by = "allcells_clusters")
DimPlot(all_mon_cells, reduction = "umap", group.by = "pca25_res.0.6")
DimPlot(all_mon_cells, reduction = "umap", group.by = "Donor")
DimPlot(all_mon_cells, reduction = "umap", group.by = "Phase")
FeaturePlot(all_mon_cells, reduction = "umap", features = c("MKI67", "ALB", "S100A8", "COLEC11",
                                                            "NKG7", "TRGC1", "PTPRC", "EPCAM",
                                                            "CD14", "TRAC", "CD3E", "CD8A"))

Clustering and markers

all_mon_cells = FindNeighbors(all_mon_cells, reduction = "pca", dims = 1:25,
                            prune.SNN = 1/5, force.recalc = T, graph.name = "pca25")
all_mon_cells = FindClusters(all_mon_cells, algorithm = 2, verbose = F, graph.name = "pca25",
                           resolution = seq(0.1, 2, 0.1))
DimPlot(all_mon_cells, reduction = "umap", group.by = "pca25_res.0.5", label = T)
DimPlot(all_mon_cells, reduction = "umap", group.by = "allcells_clusters", label = T)
DimPlot(all_mon_cells, reduction = "umap", group.by = "Donor", label = F)
DimPlot(all_mon_cells, reduction = "umap", group.by = "Condition", label = F)

all_mon_cells = SetIdent(all_mon_cells, value = "pca25_res.0.5")
mk_mon = FindAllMarkers(all_mon_cells, logfc.threshold = 0.2, pseudocount.use = 0.1)
write.csv(mk_mon[mk_mon$p_val_adj<=0.05,], 
          file = "results/immune/markers_mon_subpop_all.csv", row.names = T, quote = F)

saveRDS(mk_mon, file = "./results/immune/clust_markers_mon.RDS")

mk61 = FindMarkers(all_mon_cells, ident.1 = "6", ident.2 = "1", 
                   logfc.threshold = 0.2, pseudocount.use = 0.1)
mk21 = FindMarkers(all_mon_cells, ident.1 = "2", ident.2 = "1", 
                   logfc.threshold = 0.2, pseudocount.use = 0.1)

Add annotations

new_m_labs = c("0" = "Kupffer cells",
               "1" = "cDC2",
               "2" = "Monocytes (IGSF21+ GPR34+)", # similar to those identified here https://www.nature.com/articles/s41586-020-2922-4 
               "3" = "Macrophages (HES4+)", # very similar to 5, HES4 is one of its most unique markers
               "4" = "Kupffer cells (SUCNR1+)", # disproves https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1986575/; these KC are inflammatory/antiviral
               "5" = "Macrophages",
               "6" = "Monocytes (TREM2+ CD9+)", # in ramachandran et al, assoc with fibrotic scars
               ## in their projection, also close to KC
               "7" = "cDC2",
               "8" = "cDC1",
               "9" = "Monocytes (secretory)", # related to 2 and 6
               "10" = "activated DCs") # CD80, CD86, CCR7

all_mon_cells$mono_annot = new_m_labs[as.character(all_mon_cells$pca25_res.0.5)]

Put annotations on single immune cell object

Make dataframe with new annotations

newannot_l = list(ldf = all_l_cells@meta.data[,c("allcells_clusters", "lymphoid_annot")],
                  tdf = all_t_cells@meta.data[,c("allcells_clusters", "t_annot")],
                  mdf = all_m_cells@meta.data[,c("allcells_clusters", "mye_annot")],
                  mondf = all_mon_cells@meta.data[,c("allcells_clusters", "mono_annot")])
for(n in names(newannot_l)){
  newannot_l[[n]]$cells = rownames(newannot_l[[n]])
}

newannot_df = Reduce(function(x,y){merge(x,y, by = "cells", all = T)}, newannot_l)[,c(1,3,5,7,9)]

newannot_df$t_annot[is.na(newannot_df$t_annot)] = newannot_df$lymphoid_annot[is.na(newannot_df$t_annot)]
newannot_df$mono_annot[is.na(newannot_df$mono_annot)] = newannot_df$mye_annot[is.na(newannot_df$mono_annot)]
newannot_df$immune_annot = newannot_df$t_annot
newannot_df$immune_annot[is.na(newannot_df$immune_annot)] = newannot_df$mono_annot[is.na(newannot_df$immune_annot)]

newannot_df = data.frame(row.names = newannot_df$cells, 
                         immune_annot = newannot_df$immune_annot)

Add to the immune Seurat object

all_imm_cells = AddMetaData(all_imm_cells, metadata = newannot_df)
# the original "Dividing cells" will be relabeled "Dividing T/NK cells",
## and the newly annotated "Dividing NK cells" will be renamed to match this
all_imm_cells$immune_annot[is.na(all_imm_cells$immune_annot)] = "Dividing T/NK cells"
all_imm_cells$immune_annot[all_imm_cells$immune_annot=="Dividing NK cells"] = "Dividing T/NK cells"
all_imm_cells$immune_annot[all_imm_cells$immune_annot=="Hepatocytes"] = "Hepatocyte-Monocyte interaction"

DimPlot(all_l_cells, group.by = "lymphoid_annot", reduction = "umap", label = T)+NoLegend()+ggtitle("Lymphoid cells")
DimPlot(all_t_cells, group.by = "t_annot", reduction = "umap", label = T)+NoLegend()+ggtitle("T and NK cells (subset of Lymphoid)")
DimPlot(all_m_cells, group.by = "mye_annot", reduction = "umap", label = T)+NoLegend()+ggtitle("Myeloid cells")
DimPlot(all_mon_cells, group.by = "mono_annot", reduction = "umap", label = T)+NoLegend()+ggtitle("Monocytes cells (subset of Myeloid)")
DimPlot(all_imm_cells, group.by = "allcells_clusters", reduction = "umap", label = T)+NoLegend()+ggtitle("Immune cells (original annotation)")
DimPlot(all_imm_cells, group.by = "immune_annot", reduction = "umap", label = T)+NoLegend()+ggtitle("Immune cells (new detailed annotation)")

Save Seurat objects

saveRDS(all_l_cells, file = "results/immune/all_l_cells.RDS")
saveRDS(all_t_cells, file = "results/immune/all_t_cells.RDS")
saveRDS(all_m_cells, file = "results/immune/all_m_cells.RDS")
saveRDS(all_mon_cells, file = "results/immune/all_mon_cells.RDS")
saveRDS(all_imm_cells, file = "results/immune/all_imm_cells.RDS")

Analysis of immune cell types

Load data

all_l_cells = readRDS(file = "results/immune/all_l_cells.RDS")
all_t_cells = readRDS(file = "results/immune/all_t_cells.RDS")
all_m_cells = readRDS(file = "results/immune/all_m_cells.RDS")
all_mon_cells = readRDS(file = "results/immune/all_mon_cells.RDS")
all_imm_cells = readRDS(file = "results/immune/all_imm_cells.RDS")

Markers

all_l_cells = readRDS(file = "results/immune/all_l_cells.RDS")
all_t_cells = readRDS(file = "results/immune/all_t_cells.RDS")
all_m_cells = readRDS(file = "results/immune/all_m_cells.RDS")
all_mon_cells = readRDS(file = "results/immune/all_mon_cells.RDS")
all_imm_cells = readRDS(file = "results/immune/all_imm_cells.RDS")

Plot UMAPs

mk_lcells = read.csv("results/immune/markers_lymphoid_subpop_all.csv", 
                     row.names = 1, header = T)
new_l_labs = c("0" = "ab-T cells 1",
               "1" = "NK/gd-T cells",
               "2" = "ab-T cells 2",
               "3" = "Infiltrating NK cells", # PTGDS,CX3CR1 (infilt)
               "4" = "IgA+ Plasma cells",
               "5" = "B cells",
               "6" = "IgG+ Plasma cells",
               "7" = "Dividing NK cells",
               "8" = "ab-T cells (stress)")
mk_lcells$annot = new_l_labs[as.character(mk_lcells$cluster)]

mk_tcells = read.csv("results/immune/markers_t_subpop_all.csv", 
                     row.names = 1, header = T)
new_t_labs = c("0" = "NK cells 1",
               "1" = "TRM cells", # may have CD4 and CD8; ITGA1
               "2" = "MAIT cells 2", # CXCR6, CCR6, CCR5, some RORC (not DE with the ILC3), some ZBTB16
               "3" = "NK cells 2",
               "4" = "NK cells 3",
               "5" = "CD8 ab-T cells 1",
               "6" = "MAIT cells 1", # hard to be sure, but has many hallmarks, even higher CD8A
               "7" = "Infiltrating NK cells",
               "8" = "Naive CD4+ T cells", 
               "9" = "gd-T cells", # CD3+ vs cl7, TRDC/TRG
               "10" = "CD8 ab-T cells 2",
               "11" = "CD8 ab-T cells (stress)",
               "12" = "Treg",
               "13" = "ILC3") # KIT, AHR, RORC, no CD3/CD8/CD4
mk_tcells$annot = new_t_labs[as.character(mk_tcells$cluster)]

mk_mcells = read.csv("results/immune/markers_m_subpop_all.csv", 
                     row.names = 1, header = T)
new_m_labs = c("0" = "Kupffer cells",
               "1" = "Monocytes/cDCs",
               "2" = "Macrophages",
               "3" = "Monocytes/cDCs",
               "4" = "Kupffer cells",
               "5" = "Monocytes/cDCs",
               "6" = "Monocytes/cDCs",
               "7" = "Macrophages",
               "8" = "Monocytes/cDCs",
               "9" = "cDC1",
               "10" = "pDCs",
               "11" = "pDCs",
               "12" = "Dividing cDCs",
               "13" = "Kupffer cells",
               "14" = "Hepatocytes")
mk_mcells$annot = new_m_labs[as.character(mk_mcells$cluster)]

mk_monocells = read.csv("results/immune/markers_mon_subpop_all.csv", 
                        row.names = 1, header = T)
new_m_labs = c("0" = "Kupffer cells",
               "1" = "cDC2",
               "2" = "Monocytes (IGSF21+ GPR34+)", # similar to those identified here https://www.nature.com/articles/s41586-020-2922-4 
               "3" = "Macrophages (HES4+)", # very similar to 5, HES4 is one of its most unique markers
               "4" = "Kupffer cells (SUCNR1+)", # disproves https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1986575/; these KC are inflammatory/antiviral
               "5" = "Macrophages",
               "6" = "Monocytes (TREM2+ CD9+)", # in ramachandran et al, assoc with fibrotic scars
               ## in their projection, also close to KC
               "7" = "cDC2",
               "8" = "cDC1",
               "9" = "Monocytes (secretory)", # related to 2 and 6
               "10" = "activated DCs") # CD80, CD86, CCR7
mk_monocells$annot = new_m_labs[as.character(mk_monocells$cluster)]
imm_df = data.frame(Embeddings(all_imm_cells, reduction = "umap"))
imm_df$all_annot = all_imm_cells$immune_annot
imm_df$simp_annot = imm_df$all_annot
imm_df$simp_annot[imm_df$simp_annot %in% c("ab-T cells (stress)", 
                                           "Hepatocyte-Monocyte interaction")] = "Non annotated cells"
mono = unique(imm_df$all_annot)[c(2,4:9,11:12,31)]
imm_df$simp_annot[imm_df$simp_annot %in% mono] = "Monocytes/Macrophages/cDCs"
tnk = unique(imm_df$all_annot)[c(17:20,22:30,32)]
imm_df$simp_annot[imm_df$simp_annot %in% tnk] = "T cells/ILCs"

cols = MetBrewer::met.brewer("Egypt", length(unique(imm_df$simp_annot)))
names(cols) = unique(imm_df$simp_annot)
cols["Non annotated cells"] = "grey80"

imm_df = imm_df[sample(1:nrow(imm_df), nrow(imm_df), replace = F),]
ggplot()+
  geom_point(aes(x = UMAP_1, y = UMAP_2, colour = simp_annot), 
             imm_df, size = 0.35)+
  guides(colour = guide_legend(override.aes = list(size = 3)))+
  scale_colour_manual(values = cols[order(names(cols))])+
  theme_void()+
  theme(aspect.ratio = 1,
        legend.text = element_text(size = 6),
        legend.title = element_blank())

t_df = data.frame(Embeddings(all_t_cells, reduction = "umap"))
t_df$all_annot = all_t_cells$t_annot

cols = MetBrewer::met.brewer("Juarez", length(unique(t_df$all_annot)))
names(cols) = unique(t_df$all_annot)

t_df = t_df[sample(1:nrow(t_df), nrow(t_df), replace = F),]
ggplot()+
  geom_point(aes(x = UMAP_1, y = UMAP_2, colour = all_annot), 
             t_df, size = 0.35)+
  guides(colour = guide_legend(override.aes = list(size = 3)))+
  scale_colour_manual(values = cols[order(names(cols))])+
  theme_void()+
  theme(aspect.ratio = 1,
        legend.text = element_text(size = 6),
        legend.title = element_blank())

Save data for UMAPs

m_df = data.frame(Embeddings(all_mon_cells, reduction = "umap"))
m_df$all_annot = all_mon_cells$mono_annot

cols = MetBrewer::met.brewer("Klimt", length(unique(m_df$all_annot)))
names(cols) = unique(m_df$all_annot)

m_df = m_df[sample(1:nrow(m_df), nrow(m_df), replace = F),]
ggplot()+
  geom_point(aes(x = UMAP_1, y = UMAP_2, colour = all_annot), 
             m_df, size = 0.35)+
  guides(colour = guide_legend(override.aes = list(size = 3)))+
  scale_colour_manual(values = cols[order(names(cols))])+
  theme_void()+
  theme(aspect.ratio = 1,
        legend.text = element_text(size = 6),
        legend.title = element_blank())

Prepare marker heatmaps

saveRDS(m_df, file = "results/cirrhosis/umap_m_df.RDS")
saveRDS(t_df, file = "results/cirrhosis/umap_t_df.RDS")
saveRDS(imm_df, file = "results/cirrhosis/umap_imm_df.RDS")

Save matrices for myeloid and lymphoid markers

topmk_l = mk_lcells %>%
  group_by(annot) %>%
  top_n(n = 100, wt = avg_log2FC)
Registered S3 method overwritten by 'cli':
  method     from         
  print.boxx spatstat.geom
topmk_t = mk_tcells %>%
  group_by(annot) %>%
  top_n(n = 100, wt = avg_log2FC)
topmk_m = mk_mcells%>%
  group_by(annot) %>%
  top_n(n = 100, wt = avg_log2FC)
topmk_mono = mk_monocells%>%
  group_by(annot) %>%
  top_n(n = 100, wt = avg_log2FC)

mk_use = c("PTGDS","CX3CR1", "KIT", "AHR", "RORC", "CD3E", "CCR7", "MKI67", "CD44", "IL7R",
           "CD80", "CD86", "TREM2", "CD9", "HES4", "IGSF21", "GPR34", "IGHG1", "CD8A", "CD5L",
           "CD4", "FOXP3", "CTLA4", "CD79A", "IGHA1", "IGHM", "CXCL10", "LYVE1", "MARCO",
           "S100A8","LYZ","SELL","IFI27","FCER1A", "ITGA1", "THEMIS","S100B",
           "TRAC", "TRBC1", "TRDC", "TRGC1", "KLRC2", "CCL19", 
           "IDO1", "CLEC9A", "IL2RB", "CXCR6", "CCR6", "CCR5", "RORC", "ZBTB16")

mk_m = mk_use[mk_use %in% topmk_mono$gene | mk_use %in% topmk_m$gene]
mk_l = mk_use[mk_use %in% topmk_l$gene | mk_use %in% topmk_t$gene]

topmk_l = mk_lcells %>%
  group_by(annot) %>%
  top_n(n = 3, wt = avg_log2FC)
topmk_l_l = tapply(topmk_l$gene, topmk_l$annot, function(x) x[1:3])
topmk_t = mk_tcells %>%
  group_by(annot) %>%
  top_n(n = 3, wt = avg_log2FC)
topmk_t_l = tapply(topmk_t$gene, topmk_t$annot, function(x) x[1:3])
topmk_m = mk_mcells%>%
  group_by(annot) %>%
  top_n(n = 3, wt = avg_log2FC)
topmk_m_l = tapply(topmk_m$gene, topmk_m$annot, function(x) x[1:3])
topmk_mono = mk_monocells%>%
  group_by(annot) %>%
  top_n(n = 3, wt = avg_log2FC)
topmk_mono_l = tapply(topmk_mono$gene, topmk_mono$annot, function(x) x[1:3])

rem_ct = c("ab-T cells (stress)", "CD8 ab-T cells (stress)")
id_m = unique(names(c(topmk_m_l, topmk_mono_l)))
id_m = id_m[id_m %in% all_imm_cells$immune_annot & !id_m %in% rem_ct]
feat_m = unique(c(unlist(c(topmk_m_l, topmk_mono_l)[id_m]), mk_m))
feat_m = feat_m[!grepl(".",feat_m,fixed = T)]

id_l = unique(names(c(topmk_l_l, topmk_t_l)))
id_l = id_l[id_l %in% all_imm_cells$immune_annot & !id_l %in% rem_ct]
feat_l = unique(c(unlist(c(topmk_l_l, topmk_t_l)[id_l]), mk_l))
feat_l = feat_l[!grepl(".",feat_l,fixed = T)]

avg_m = AverageExpression(all_imm_cells, features = feat_m, 
                          group.by = "immune_annot")$SCT[,id_m]
Warning in PseudobulkExpression(object = object, pb.method = "average",  :
  Exponentiation yielded infinite values. `data` may not be log-normed.
avg_l = AverageExpression(all_imm_cells, features = feat_l, 
                          group.by = "immune_annot")$SCT[,id_l]
Warning in PseudobulkExpression(object = object, pb.method = "average",  :
  Exponentiation yielded infinite values. `data` may not be log-normed.
mat_m = scale(t(avg_m))
mat_l = scale(t(avg_l))

cols_pal = colorRampPalette(rev(brewer.pal(n = 9, name = "RdBu")))(100)
pheatmap::pheatmap(mat_m, clustering_method = "ward.D", color = cols_pal,
                   treeheight_col = 0, treeheight_row = 20, fontsize = 6.5)

pheatmap::pheatmap(mat_l, clustering_method = "ward.D", color = cols_pal,
                   treeheight_col = 0, treeheight_row = 20, fontsize = 6.5)

Changes in proportions between conditions

saveRDS(mat_m, file = "results/cirrhosis/mat_myeloid_markers.RDS")
saveRDS(mat_l, file = "results/cirrhosis/mat_lymphoid_markers.RDS")

Save proportions data

don_cond_df = unique(all_imm_cells@meta.data[,c("Name", "Donor", "Condition")])
tab_ctCond = table(all_imm_cells$immune_annot, all_imm_cells$Name)
tab_ctCond = tab_ctCond[!grepl("stress", rownames(tab_ctCond)) & 
                          !grepl("intera", rownames(tab_ctCond)),]
imm_props = t(t(tab_ctCond)/colSums(tab_ctCond))*100
imm_props = merge(data.frame(imm_props), don_cond_df, by.x = 2, by.y = 1)

ggplot(imm_props, aes(x = Condition, y = Freq, group = Condition, colour = Condition))+
  facet_wrap(~Var1, scales = "free")+
  geom_jitter(position = position_jitterdodge(jitter.width = 0.3, dodge.width = 1))+
  stat_summary(fun.data = mean_se, position = position_dodge(width = 1), 
               alpha = 0.35, colour = "black")+
  theme_bw()+
  theme(legend.position = "none")



tab_ctCond = table(all_imm_cells$immune_annot, all_imm_cells$Name)
tab_ctCond = tab_ctCond[!grepl("stress", rownames(tab_ctCond)) & 
                          !grepl("intera", rownames(tab_ctCond)),]
imm_cnts = tab_ctCond
imm_cnts = merge(data.frame(imm_cnts), don_cond_df, by.x = 2, by.y = 1)
pvals_l = list()
for(ct in unique(imm_cnts$Var1)){
  dfct = imm_cnts[imm_cnts$Var1==ct,]
  dfct$not = tapply(imm_cnts$Freq, imm_cnts$Var2, sum)-dfct$Freq
  dfct$tot = tapply(imm_cnts$Freq, imm_cnts$Var2, sum)
  dfct$Condition = factor(dfct$Condition, levels = c("healthy", "embolised", "regenerating"))
  mod = glm(cbind(Freq, not) ~ Condition + Donor + tot, data = dfct, family = "binomial")
  pvals_l[[ct]] = summary(mod)$coefficients[c("Conditionembolised", "Conditionregenerating"),
                                            "Pr(>|z|)"]
}
Warning: glm.fit: algorithm did not converge
Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
pvals_df = data.frame(t(data.frame(pvals_l)))
rownames(pvals_df) = names(pvals_l)
colnames(pvals_df) = gsub("Condition", "", colnames(pvals_df))
pvals_df$embolised = p.adjust(pvals_df$embolised, method = "fdr")
pvals_df$regenerating = p.adjust(pvals_df$regenerating, method = "fdr")

Load Ramachandran data

saveRDS(imm_props, file = "results/cirrhosis/imm_props_dat.RDS")
saveRDS(pvals_df, file = "results/cirrhosis/imm_props_pval.RDS")

Subset Ramachandran and get HVG

f = "results/cirrhosis/cirr_data_renorm.RDS"
if(!file.exists(f)){
  load("../../data/published/Ramachandran_liver/tissue.rdata")
  
  cirr_data = CreateSeuratObject(counts = tissue@raw.data, meta.data = tissue@meta.data)
  
  # Renormalise, since the original data doesn't look ok
  cirr_data = suppressWarnings(SCTransform(cirr_data, do.correct.umi = T, verbose = F, 
                                           vars.to.regress=c("dataset", "nCount_RNA"),
                                           variable.features.rv.th = 1, seed.use = 1,
                                           return.only.var.genes = F, 
                                           variable.features.n = NULL))
  
  
  cirr_data$anno_cond = paste0(cirr_data$annotation_indepth, "_", cirr_data$condition)
  saveRDS(cirr_data, file = f)
} else{
  cirr_data = readRDS(f)
}

Correlation with Ramachandran data

f = "results/cirrhosis/cirr_data_allimmune_renorm.RDS"
if(!file.exists(f)){
  cirr_imm_data = cirr_data[,cirr_data$annotation_lineage %in% c("MPs", "Tcells", "ILCs",
                                                                 "Bcells", "pDCs", 
                                                                 "Plasma Bcells", "Mast cells")]
  cirr_imm_data = suppressWarnings(SCTransform(cirr_imm_data, do.correct.umi = T, verbose = F, 
                         vars.to.regress=c("dataset", "nCount_RNA"),
                         variable.features.rv.th = 1, seed.use = 1,
                         return.only.var.genes = F, variable.features.n = NULL))
  saveRDS(cirr_imm_data, file = "results/cirrhosis/cirr_data_allimmune_renorm.RDS")
} else{
  cirr_imm_data = readRDS(f)
}

# MPs only
f = "results/cirrhosis/cirr_data_MPs_renorm.RDS"
if(!file.exists(f)){
  cirr_mps_data = cirr_data[,cirr_data$annotation_lineage %in% c("MPs")]
  cirr_mps_data = suppressWarnings(SCTransform(cirr_mps_data, do.correct.umi = T, verbose = F, 
                         vars.to.regress=c("dataset", "nCount_RNA"),
                         variable.features.rv.th = 1, seed.use = 1,
                         return.only.var.genes = F, variable.features.n = NULL))
  saveRDS(cirr_mps_data, file = "results/cirrhosis/cirr_data_MPs_renorm.RDS")
} else{
  cirr_mps_data = readRDS(f)
}

# Tcells and ILCs only
f = "results/cirrhosis/cirr_data_tcells_renorm.RDS"
if(!file.exists(f)){
  cirr_tce_data = cirr_data[,cirr_data$annotation_lineage %in% c("Tcells", "ILCs")]
  cirr_tce_data = suppressWarnings(SCTransform(cirr_tce_data, do.correct.umi = T, verbose = F, 
                         vars.to.regress=c("dataset", "nCount_RNA"),
                         variable.features.rv.th = 1, seed.use = 1,
                         return.only.var.genes = F, variable.features.n = NULL))
  saveRDS(cirr_tce_data, file = f)
} else{
  cirr_tce_data = readRDS(f)
}
LS0tCnRpdGxlOiAiSW1tdW5lIHBvcHVsYXRpb24gYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgoKIyBHZW5lcmFsIFNldHVwClNldHVwIGNodW5rCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA4KQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgoIi4uIikpCmtuaXRyOjpvcHRzX2tuaXQkZ2V0KCJyb290LmRpciIpCmBgYAoKU2V0dXAgcmV0aWN1bGF0ZQoKYGBge3J9CmxpYnJhcnkocmV0aWN1bGF0ZSkKa25pdHI6OmtuaXRfZW5naW5lcyRzZXQocHl0aG9uID0gcmV0aWN1bGF0ZTo6ZW5nX3B5dGhvbikKcHlfYXZhaWxhYmxlKGluaXRpYWxpemUgPSBGQUxTRSkKdXNlX3B5dGhvbihTeXMud2hpY2goInB5dGhvbiIpKQpweV9jb25maWcoKQpgYGAKCkxvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGRlc3RpbnkpCmxpYnJhcnkocGx5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKTG9hZCBkYXRhIChmcm9tIGFsbCBjZWxscykKCmBgYHtyfQphbGxjZWxsc19jc3MgPSByZWFkUkRTKGZpbGUgPSAiZGF0YS9wcm9jZXNzZWQvYWxsY2VsbHNfY3NzLlJEUyIpCmBgYAoKT3RoZXIgZnVuY3Rpb25zCgpgYGB7cn0KIyBQbG90IGNvcnJlbGF0aW9ucwpwbG90Q29yciA9IGZ1bmN0aW9uKGNvcnQsIHNwMSwgc3AyKXsKICAjIGNsdXN0ZXIgYW5kIG9yZGVyIGxhYmVscwogIGhjciA9IGhjbHVzdChkaXN0KGNvcnQkciksIG1ldGhvZCA9ICJ3YXJkLkQyIikKICBoY2MgPSBoY2x1c3QoZGlzdCh0KGNvcnQkcikpLCBtZXRob2QgPSAid2FyZC5EMiIpCiAgaGNyID0gaGNyJGxhYmVsc1toY3Ikb3JkZXJdCiAgaGNjID0gaGNjJGxhYmVsc1toY2Mkb3JkZXJdCgogICMgcmVzaGFwaW5nIHRoZSBjb3JyZWxhdGlvbnMKICBwbG90X2RmID0gcmVzaGFwZTI6Om1lbHQoY29ydCRyKQogIHBsb3RfZGYkVmFyMSA9IGZhY3RvcihwbG90X2RmJFZhcjEsIGxldmVscyA9IHJldihoY3IpKQogIHBsb3RfZGYkVmFyMiA9IGZhY3RvcihwbG90X2RmJFZhcjIsIGxldmVscyA9IGhjYykKCiAgIyBhZGQgcHZhbHVlIGFuZCBtYXggY29yIGluZm9yCiAgcGxvdF9kZiRwYWRqID0gLWxvZzEwKHJlc2hhcGUyOjptZWx0KGNvcnQkcC5hZGorbWluKGNvcnQkcC5hZGpbY29ydCRwLmFkaj4wXSkvMTApJHZhbHVlKQogIHBsb3RfZGYkcm93bWF4ID0gYXBwbHkoUmVkdWNlKGNiaW5kLCBsYXBwbHkobmFtZXMoY29ydCRtYXhyb3cpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24obikgcGxvdF9kZiRWYXIxPT1uICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9kZiRWYXIyPT1jb2xuYW1lcyhjb3J0JHIpW2NvcnQkbWF4cm93W25dXSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgMSwgYW55KQogIHBsb3RfZGYkY29sbWF4ID0gYXBwbHkoUmVkdWNlKGNiaW5kLCBsYXBwbHkobmFtZXMoY29ydCRtYXhjb2wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24obikgcGxvdF9kZiRWYXIyPT1uICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9kZiRWYXIxPT1yb3duYW1lcyhjb3J0JHIpW2NvcnQkbWF4Y29sW25dXSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgMSwgYW55KQogIHBsb3RfZGYkbWFya2NvbCA9IHBsb3RfZGYkdmFsdWU+cXVhbnRpbGUocGxvdF9kZiR2YWx1ZSwgMC45OCkKCiAgIyBnZXR0aW5nIGEgY29sb3Vyc2NhbGUgd2hlcmUgMCBpcyB3aGl0ZSBpbiB0aGUgbWlkZGxlLCBhbmQgaW50ZW5zaXR5IGxldmVsZWQgYnkgbWF4KGFicyh2YWx1ZSkpCiAgY29scyA9IGNvbG9yUmFtcFBhbGV0dGUoYyhyZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDksICJCbHVlcyIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg5LCAiUmVkcyIpKSkoMTAxKQogIGJyID0gc2VxKC1tYXgoYWJzKGNvcnQkcikpLCBtYXgoYWJzKGNvcnQkcikpLCBsZW5ndGgub3V0ID0gMTAxKQogIGNvbHMgPSBjb2xzWyEoYnI+bWF4KGNvcnQkcikgfCBicjxtaW4oY29ydCRyKSldCgogIGNvcnBsb3QgPSBnZ3Bsb3QoKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBsb3RfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gdmFsdWUsIHNpemUgPSBwYWRqKSwKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwbG90X2RmW3Bsb3RfZGYkcm93bWF4LF0sIG1hcHBpbmcgPSBhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBzaXplID0gcGFkaiksCiAgICAgICAgICAgICAgIHNoYXBlID0gIuKAlCIsIHNob3cubGVnZW5kID0gRiwgY29sb3VyID0gImdyZXkxMCIpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGxvdF9kZltwbG90X2RmJGNvbG1heCxdLCBtYXBwaW5nID0gYWVzKHggPSBWYXIyLCB5ID0gVmFyMSwgc2l6ZSA9IHBhZGopLAogICAgICAgICAgICAgICBzaGFwZSA9ICJ8Iiwgc2hvdy5sZWdlbmQgPSBGLCBjb2xvdXIgPSAiZ3JleTEwIikrCiAgICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwwLjcpKSsKICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLDAuNykpKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oYnJlYWtzID0gc2lnbmlmKGMobWluKGNvcnQkcikrMC4wMDUsIDAsIG1heChjb3J0JHIpLTAuMDA1KSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHNjYWxlczo6cmVzY2FsZShjKG1pbihiciksIDAsIG1heChicikpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91cnMgPSBjb2xzKSsKICAgIGxhYnMoeCA9IHNwMiwgeSA9IHNwMSwgZmlsbCA9ICJTcGVhcm1hbidzXG5yaG8iLCBzaXplID0gIi1sb2cxMFxuYWRqLiBwLXZhbHVlIikrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIpLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKCiAgcmV0dXJuKGNvcnBsb3QpCn0KYGBgCgoKCiMgSW1tdW5lIGNlbGwgc3Vic2V0dGluZwpTdWJzZXQgaW1tdW5lIGNlbGxzCgpgYGB7cn0KIyB0aGUgZGl2aWRpbmcgY2VsbHMgYXJlIFQvTkssIHdpbGwgdXBkYXRlIGxhYmVsIGF0IHRoZSBlbmQKaW1tdW5lX3BvcHMgPSBjKCJhYi1UIGNlbGxzIiwgImdkLVQgY2VsbHMiLCAKICAgICAgICAgICAgICAgICJQbGFzbWFibGFzdHMiLCAiY0RDcyAyIiwgCiAgICAgICAgICAgICAgICAiTWFjcm9waGFnZXMiLCAiS3VwZmZlciBjZWxscyIsCiAgICAgICAgICAgICAgICAiQiBjZWxscyIsICAiY0RDcyAxIiwgInBEQ3MiLCAiRGl2aWRpbmcgY2VsbHMiKQphbGxfaW1tX2NlbGxzID0gYWxsY2VsbHNfY3NzWyxhbGxjZWxsc19jc3NAbWV0YS5kYXRhJGFsbGNlbGxzX2NsdXN0ZXJzICVpbiUgaW1tdW5lX3BvcHNdCmFsbF9pbW1fY2VsbHMgPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGFsbF9pbW1fY2VsbHMsIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3M9YygidW5pcXVlX25hbWUiLCJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQphbGxfaW1tX2NlbGxzID0gUnVuUENBKGFsbF9pbW1fY2VsbHMsIHZlcmJvc2UgPSBGKQphbGxfaW1tX2NlbGxzID0gUnVuVU1BUChhbGxfaW1tX2NlbGxzLCBkaW1zID0gMToyNSwgdmVyYm9zZSA9IEYpCkRpbVBsb3QoYWxsX2ltbV9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJDb25kaXRpb24iKQpEaW1QbG90KGFsbF9pbW1fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYWxsY2VsbHNfY2x1c3RlcnMiKQpEaW1QbG90KGFsbF9pbW1fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiRG9ub3IiKQpEaW1QbG90KGFsbF9pbW1fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiUGhhc2UiKQpGZWF0dXJlUGxvdChhbGxfaW1tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGZlYXR1cmVzID0gYygiTUtJNjciLCAiQUxCIiwgIlMxMDBBOCIsICJDT0xFQzExIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNMRUMxQiIsICJDTEVDMTRBIiwgIlBUUFJDIiwgIkVQQ0FNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMTQiLCAiVFJBQyIsICJDRDNFIiwgIkNEOEEiKSkKYGBgCgoKIyMgTHltcGhvaWQgY2VsbCBhbmFseXNpcwpTdWJzZXQgTHltcGhvaWQgY2VsbHMKCmBgYHtyfQppbW11bmVfcG9wcyA9IGMoImFiLVQgY2VsbHMiLCAiZ2QtVCBjZWxscyIsICJQbGFzbWFibGFzdHMiLCAiQiBjZWxscyIpCmFsbF9sX2NlbGxzID0gYWxsY2VsbHNfY3NzWyxhbGxjZWxsc19jc3NAbWV0YS5kYXRhJGFsbGNlbGxzX2NsdXN0ZXJzICVpbiUgaW1tdW5lX3BvcHNdCmFsbF9sX2NlbGxzID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShhbGxfbF9jZWxscywgZG8uY29ycmVjdC51bWkgPSBULCB2ZXJib3NlID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCmFsbF9sX2NlbGxzID0gUnVuUENBKGFsbF9sX2NlbGxzLCB2ZXJib3NlID0gRikKYWxsX2xfY2VsbHMgPSBSdW5VTUFQKGFsbF9sX2NlbGxzLCBkaW1zID0gMToyNSwgdmVyYm9zZSA9IEYpCkRpbVBsb3QoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIikKRGltUGxvdChhbGxfbF9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJhbGxjZWxsc19jbHVzdGVycyIpCkRpbVBsb3QoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiRG9ub3IiKQpEaW1QbG90KGFsbF9sX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIlBoYXNlIikKRmVhdHVyZVBsb3QoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZmVhdHVyZXMgPSBjKCJNS0k2NyIsICJBTEIiLCAiUzEwMEE4IiwgIkNPTEVDMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTktHNyIsICJUUkdDMSIsICJQVFBSQyIsICJFUENBTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDE0IiwgIlRSQUMiLCAiQ0QzRSIsICJDRDhBIikpCmBgYAoKQ2x1c3RlciBhbmQgZ2V0IG1hcmtlcnMKCmBgYHtyfQphbGxfbF9jZWxscyA9IEZpbmROZWlnaGJvcnMoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBydW5lLlNOTiA9IDEvNSwgZm9yY2UucmVjYWxjID0gVCwgZ3JhcGgubmFtZSA9ICJwY2EyNSIpCmFsbF9sX2NlbGxzID0gRmluZENsdXN0ZXJzKGFsbF9sX2NlbGxzLCBhbGdvcml0aG0gPSAyLCB2ZXJib3NlID0gRiwgZ3JhcGgubmFtZSA9ICJwY2EyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBzZXEoMC4xLCAyLCAwLjEpKQpEaW1QbG90KGFsbF9sX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInBjYTI1X3Jlcy4wLjQiLCBsYWJlbCA9IFQpCkRpbVBsb3QoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiRG9ub3IiLCBsYWJlbCA9IEYpCkRpbVBsb3QoYWxsX2xfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgbGFiZWwgPSBGKQoKYWxsX2xfY2VsbHMgPSBTZXRJZGVudChhbGxfbF9jZWxscywgdmFsdWUgPSAicGNhMjVfcmVzLjAuNCIpCm1rX2xjZWxscyA9IEZpbmRBbGxNYXJrZXJzKGFsbF9sX2NlbGxzLCBsb2dmYy50aHJlc2hvbGQgPSAwLjIsIHBzZXVkb2NvdW50LnVzZSA9IDAuMSkKd3JpdGUuY3N2KG1rX2xjZWxsc1tta19sY2VsbHMkcF92YWxfYWRqPD0wLjA1LF0sIAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL2ltbXVuZS9tYXJrZXJzX2x5bXBob2lkX3N1YnBvcF9hbGwuY3N2Iiwgcm93Lm5hbWVzID0gVCwgcXVvdGUgPSBGKQoKCm1rMDIgPSBGaW5kTWFya2VycyhhbGxfbF9jZWxscywgaWRlbnQuMSA9ICIwIiwgaWRlbnQuMiA9ICIyIiwgCiAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjIsIHBzZXVkb2NvdW50LnVzZSA9IDAuMSkKYGBgCgpBZGQgYW5ub3RhdGlvbnMKCmBgYHtyfQpuZXdfbF9sYWJzID0gYygiMCIgPSAiYWItVCBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjEiID0gIk5LL2dkLVQgY2VsbHMiLAogICAgICAgICAgICAgICAiMiIgPSAiYWItVCBjZWxscyAyIiwKICAgICAgICAgICAgICAgIjMiID0gIkluZmlsdHJhdGluZyBOSyBjZWxscyIsICMgUFRHRFMsQ1gzQ1IxIChpbmZpbHQpCiAgICAgICAgICAgICAgICI0IiA9ICJJZ0ErIFBsYXNtYSBjZWxscyIsCiAgICAgICAgICAgICAgICI1IiA9ICJCIGNlbGxzIiwKICAgICAgICAgICAgICAgIjYiID0gIklnRysgUGxhc21hIGNlbGxzIiwKICAgICAgICAgICAgICAgIjciID0gIkRpdmlkaW5nIE5LIGNlbGxzIiwKICAgICAgICAgICAgICAgIjgiID0gImFiLVQgY2VsbHMgKHN0cmVzcykiKQoKYWxsX2xfY2VsbHMkbHltcGhvaWRfYW5ub3QgPSBuZXdfbF9sYWJzW2FzLmNoYXJhY3RlcihhbGxfbF9jZWxscyRwY2EyNV9yZXMuMC40KV0KYGBgCgpTdWJzZXQgVC9OSyBjZWxscwoKYGBge3J9CmltbXVuZV9wb3BzID0gYygiYWItVCBjZWxscyAxIiwgImFiLVQgY2VsbHMgMiIsICJOSy9nZC1UIGNlbGxzIiwgIkluZmlsdHJhdGluZyBOSyBjZWxscyIpCmFsbF90X2NlbGxzID0gYWxsX2xfY2VsbHNbLGFsbF9sX2NlbGxzQG1ldGEuZGF0YSRseW1waG9pZF9hbm5vdCAlaW4lIGltbXVuZV9wb3BzXQphbGxfdF9jZWxscyA9IHN1cHByZXNzV2FybmluZ3MoU0NUcmFuc2Zvcm0oYWxsX3RfY2VsbHMsIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3M9YygidW5pcXVlX25hbWUiLCJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQphbGxfdF9jZWxscyA9IFJ1blBDQShhbGxfdF9jZWxscywgdmVyYm9zZSA9IEYpCmFsbF90X2NlbGxzID0gUnVuVU1BUChhbGxfdF9jZWxscywgZGltcyA9IDE6MjUsIHZlcmJvc2UgPSBGKQpEaW1QbG90KGFsbF90X2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIpCkRpbVBsb3QoYWxsX3RfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYWxsY2VsbHNfY2x1c3RlcnMiKQpEaW1QbG90KGFsbF90X2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImx5bXBob2lkX2Fubm90IikKRGltUGxvdChhbGxfdF9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJEb25vciIpCkRpbVBsb3QoYWxsX3RfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiUGhhc2UiKQpGZWF0dXJlUGxvdChhbGxfdF9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlcyA9IGMoIk1LSTY3IiwgIkFMQiIsICJTMTAwQTgiLCAiQ09MRUMxMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOS0c3IiwgIlRSR0MxIiwgIlBUUFJDIiwgIkVQQ0FNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMTQiLCAiVFJBQyIsICJDRDNFIiwgIkNEOEEiKSkKYGBgCgpDbHVzdGVyIGFuZCBnZXQgbWFya2VycwoKYGBge3J9CmFsbF90X2NlbGxzID0gRmluZE5laWdoYm9ycyhhbGxfdF9jZWxscywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmUuU05OID0gMS81LCBmb3JjZS5yZWNhbGMgPSBULCBncmFwaC5uYW1lID0gInBjYTI1IikKYWxsX3RfY2VsbHMgPSBGaW5kQ2x1c3RlcnMoYWxsX3RfY2VsbHMsIGFsZ29yaXRobSA9IDIsIHZlcmJvc2UgPSBGLCBncmFwaC5uYW1lID0gInBjYTI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IHNlcSgwLjEsIDIsIDAuMSkpCkRpbVBsb3QoYWxsX3RfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAicGNhMjVfcmVzLjAuOSIsIGxhYmVsID0gVCkKRGltUGxvdChhbGxfdF9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJEb25vciIsIGxhYmVsID0gRikKRGltUGxvdChhbGxfdF9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJDb25kaXRpb24iLCBsYWJlbCA9IEYpCkRpbVBsb3QoYWxsX3RfY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAidF9hbm5vdCIsIGxhYmVsID0gVCkKCmFsbF90X2NlbGxzID0gU2V0SWRlbnQoYWxsX3RfY2VsbHMsIHZhbHVlID0gInBjYTI1X3Jlcy4wLjkiKQpta190Y2VsbHMgPSBGaW5kQWxsTWFya2VycyhhbGxfdF9jZWxscywgbG9nZmMudGhyZXNob2xkID0gMC4yLCBwc2V1ZG9jb3VudC51c2UgPSAwLjEpCndyaXRlLmNzdihta190Y2VsbHNbbWtfdGNlbGxzJHBfdmFsX2Fkajw9MC4wNSxdLCAKICAgICAgICAgIGZpbGUgPSAicmVzdWx0cy9pbW11bmUvbWFya2Vyc190X3N1YnBvcF9hbGwuY3N2Iiwgcm93Lm5hbWVzID0gVCwgcXVvdGUgPSBGKQoKc2F2ZVJEUyhta190Y2VsbHMsIGZpbGUgPSAiLi9yZXN1bHRzL2ltbXVuZS9jbHVzdF9tYXJrZXJzX3QuUkRTIikKCm1rOTcgPSBGaW5kTWFya2VycyhhbGxfdF9jZWxscywgaWRlbnQuMSA9ICI5IiwgaWRlbnQuMiA9ICI3IiwgCiAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjIsIHBzZXVkb2NvdW50LnVzZSA9IDAuMSkKbWsyNiA9IEZpbmRNYXJrZXJzKGFsbF90X2NlbGxzLCBpZGVudC4xID0gIjIiLCBpZGVudC4yID0gIjYiLAogICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yLCBwc2V1ZG9jb3VudC51c2UgPSAwLjEpCm1rODEzID0gRmluZE1hcmtlcnMoYWxsX3RfY2VsbHMsIGlkZW50LjEgPSAiOCIsIGlkZW50LjIgPSAiMTMiLAogICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yLCBwc2V1ZG9jb3VudC51c2UgPSAwLjEpCmBgYAoKQW5ub3RhdGUKCmBgYHtyfQojIGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUwMDc2MzAvCm5ld190X2xhYnMgPSBjKCIwIiA9ICJOSyBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjEiID0gIlRSTSBjZWxscyIsICMgbWF5IGhhdmUgQ0Q0IGFuZCBDRDg7IElUR0ExCiAgICAgICAgICAgICAgICIyIiA9ICJNQUlUIGNlbGxzIDIiLCAjIENYQ1I2LCBDQ1I2LCBDQ1I1LCBzb21lIFJPUkMgKG5vdCBERSB3aXRoIHRoZSBJTEMzKSwgc29tZSBaQlRCMTYKICAgICAgICAgICAgICAgIjMiID0gIk5LIGNlbGxzIDIiLAogICAgICAgICAgICAgICAiNCIgPSAiTksgY2VsbHMgMyIsCiAgICAgICAgICAgICAgICI1IiA9ICJDRDggYWItVCBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjYiID0gIk1BSVQgY2VsbHMgMSIsICMgaGFyZCB0byBiZSBzdXJlLCBidXQgaGFzIG1hbnkgaGFsbG1hcmtzLCBldmVuIGhpZ2hlciBDRDhBCiAgICAgICAgICAgICAgICI3IiA9ICJJbmZpbHRyYXRpbmcgTksgY2VsbHMiLAogICAgICAgICAgICAgICAiOCIgPSAiTmFpdmUgQ0Q0KyBUIGNlbGxzIiwgCiAgICAgICAgICAgICAgICI5IiA9ICJnZC1UIGNlbGxzIiwgIyBDRDMrIHZzIGNsNywgVFJEQy9UUkcKICAgICAgICAgICAgICAgIjEwIiA9ICJDRDggYWItVCBjZWxscyAyIiwKICAgICAgICAgICAgICAgIjExIiA9ICJDRDggYWItVCBjZWxscyAoc3RyZXNzKSIsCiAgICAgICAgICAgICAgICIxMiIgPSAiVHJlZyIsCiAgICAgICAgICAgICAgICIxMyIgPSAiSUxDMyIpICMgS0lULCBBSFIsIFJPUkMsIG5vIENEMy9DRDgvQ0Q0CgphbGxfdF9jZWxscyR0X2Fubm90ID0gbmV3X3RfbGFic1thcy5jaGFyYWN0ZXIoYWxsX3RfY2VsbHMkcGNhMjVfcmVzLjAuOSldCmBgYAoKCiMjIE15ZWxvaWQgY2VsbCBhbmFseXNpcwpTdWJzZXQgTXllbG9pZCBjZWxscwoKYGBge3J9CmltbXVuZV9wb3BzID0gYygiY0RDcyAyIiwgIk1hY3JvcGhhZ2VzIiwgIkt1cGZmZXIgY2VsbHMiLCAiY0RDcyAxIiwgInBEQ3MiKQphbGxfbV9jZWxscyA9IGFsbGNlbGxzX2Nzc1ssYWxsY2VsbHNfY3NzQG1ldGEuZGF0YSRhbGxjZWxsc19jbHVzdGVycyAlaW4lIGltbXVuZV9wb3BzXQphbGxfbV9jZWxscyA9IHN1cHByZXNzV2FybmluZ3MoU0NUcmFuc2Zvcm0oYWxsX21fY2VsbHMsIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3M9YygidW5pcXVlX25hbWUiLCJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQphbGxfbV9jZWxscyA9IFJ1blBDQShhbGxfbV9jZWxscywgdmVyYm9zZSA9IEYpCmFsbF9tX2NlbGxzID0gUnVuVU1BUChhbGxfbV9jZWxscywgZGltcyA9IDE6MjUsIHZlcmJvc2UgPSBGKQpEaW1QbG90KGFsbF9tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIpCkRpbVBsb3QoYWxsX21fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYWxsY2VsbHNfY2x1c3RlcnMiKQpEaW1QbG90KGFsbF9tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIkRvbm9yIikKRGltUGxvdChhbGxfbV9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJQaGFzZSIpCkZlYXR1cmVQbG90KGFsbF9tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGZlYXR1cmVzID0gYygiTUtJNjciLCAiQUxCIiwgIlMxMDBBOCIsICJDT0xFQzExIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5LRzciLCAiVFJHQzEiLCAiUFRQUkMiLCAiRVBDQU0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0QxNCIsICJUUkFDIiwgIkNEM0UiLCAiQ0Q4QSIpKQpgYGAKCkNsdXN0ZXJpbmcgYW5kIG1hcmtlcnMKCmBgYHtyfQphbGxfbV9jZWxscyA9IEZpbmROZWlnaGJvcnMoYWxsX21fY2VsbHMsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBydW5lLlNOTiA9IDEvNSwgZm9yY2UucmVjYWxjID0gVCwgZ3JhcGgubmFtZSA9ICJwY2EyNSIpCmFsbF9tX2NlbGxzID0gRmluZENsdXN0ZXJzKGFsbF9tX2NlbGxzLCBhbGdvcml0aG0gPSAyLCB2ZXJib3NlID0gRiwgZ3JhcGgubmFtZSA9ICJwY2EyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBzZXEoMC4xLCAyLCAwLjEpKQpEaW1QbG90KGFsbF9tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInBjYTI1X3Jlcy4wLjYiLCBsYWJlbCA9IFQpCkRpbVBsb3QoYWxsX21fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiRG9ub3IiLCBsYWJlbCA9IEYpCkRpbVBsb3QoYWxsX21fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgbGFiZWwgPSBGKQpEaW1QbG90KGFsbF9tX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImFsbGNlbGxzX2NsdXN0ZXJzIiwgbGFiZWwgPSBGKQoKYWxsX21fY2VsbHMgPSBTZXRJZGVudChhbGxfbV9jZWxscywgdmFsdWUgPSAicGNhMjVfcmVzLjAuNiIpCm1rX21jZWxscyA9IEZpbmRBbGxNYXJrZXJzKGFsbF9tX2NlbGxzLCBsb2dmYy50aHJlc2hvbGQgPSAwLjIsIHBzZXVkb2NvdW50LnVzZSA9IDAuMSkKd3JpdGUuY3N2KG1rX21jZWxsc1tta19tY2VsbHMkcF92YWxfYWRqPD0wLjA1LF0sIAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL2ltbXVuZS9tYXJrZXJzX21fc3VicG9wX2FsbC5jc3YiLCByb3cubmFtZXMgPSBULCBxdW90ZSA9IEYpCgpzYXZlUkRTKG1rX21jZWxscywgZmlsZSA9ICIuL3Jlc3VsdHMvaW1tdW5lL2NsdXN0X21hcmtlcnNfbXllbG9pZC5SRFMiKQoKbWsxMDExID0gRmluZE1hcmtlcnMoYWxsX21fY2VsbHMsIGlkZW50LjEgPSAiMTAiLCBpZGVudC4yID0gIjExIiwKICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yLCBwc2V1ZG9jb3VudC51c2UgPSAwLjEpCmBgYAoKQWRkIGFubm90YXRpb25zCgpgYGB7cn0KbXJrc19xID0gU291cFg6OnF1aWNrTWFya2VycyhhbGxfbV9jZWxsc0Bhc3NheXMkU0NUQGNvdW50cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxfbV9jZWxsc0BhY3RpdmUuaWRlbnQsIE4gPSAxMCkKVmlldyhtcmtzX3FbbXJrc19xJHF2YWw8PTAuMDUsXSkKCm5ld19tX2xhYnMgPSBjKCIwIiA9ICJLdXBmZmVyIGNlbGxzIiwKICAgICAgICAgICAgICAgIjEiID0gIk1vbm9jeXRlcy9jRENzIiwKICAgICAgICAgICAgICAgIjIiID0gIk1hY3JvcGhhZ2VzIiwKICAgICAgICAgICAgICAgIjMiID0gIk1vbm9jeXRlcy9jRENzIiwKICAgICAgICAgICAgICAgIjQiID0gIkt1cGZmZXIgY2VsbHMiLAogICAgICAgICAgICAgICAiNSIgPSAiTW9ub2N5dGVzL2NEQ3MiLAogICAgICAgICAgICAgICAiNiIgPSAiTW9ub2N5dGVzL2NEQ3MiLAogICAgICAgICAgICAgICAiNyIgPSAiTWFjcm9waGFnZXMiLAogICAgICAgICAgICAgICAiOCIgPSAiTW9ub2N5dGVzL2NEQ3MiLAogICAgICAgICAgICAgICAiOSIgPSAiY0RDMSIsCiAgICAgICAgICAgICAgICIxMCIgPSAicERDcyIsCiAgICAgICAgICAgICAgICIxMSIgPSAicERDcyIsCiAgICAgICAgICAgICAgICIxMiIgPSAiRGl2aWRpbmcgY0RDcyIsCiAgICAgICAgICAgICAgICIxMyIgPSAiS3VwZmZlciBjZWxscyIsCiAgICAgICAgICAgICAgICIxNCIgPSAiSGVwYXRvY3l0ZXMiKQoKYWxsX21fY2VsbHMkbXllX2Fubm90ID0gbmV3X21fbGFic1thcy5jaGFyYWN0ZXIoYWxsX21fY2VsbHMkcGNhMjVfcmVzLjAuNildCmBgYAoKU3Vic2V0IE1vbm9jeXRlcwoKYGBge3J9CmltbXVuZV9wb3BzID0gYygiMiIsICI3IiwgIjEiLCAiNiIsICI4IiwgIjMiLCAiNSIsICI0IiwgIjEzIiwgIjAiLCAiOSIpCmFsbF9tb25fY2VsbHMgPSBhbGxfbV9jZWxsc1ssYWxsX21fY2VsbHNAbWV0YS5kYXRhJHBjYTI1X3Jlcy4wLjYgJWluJSBpbW11bmVfcG9wcyAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxfbV9jZWxsc0BtZXRhLmRhdGEkYWxsY2VsbHNfY2x1c3RlcnMgJWluJSBjKCJNYWNyb3BoYWdlcyIsICJjRENzIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY0RDcyAyIiwgIkt1cGZmZXIgY2VsbHMiKV0KYWxsX21vbl9jZWxscyA9IHN1cHByZXNzV2FybmluZ3MoU0NUcmFuc2Zvcm0oYWxsX21vbl9jZWxscywgZG8uY29ycmVjdC51bWkgPSBULCB2ZXJib3NlID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCmFsbF9tb25fY2VsbHMgPSBSdW5QQ0EoYWxsX21vbl9jZWxscywgdmVyYm9zZSA9IEYpCmFsbF9tb25fY2VsbHMgPSBSdW5VTUFQKGFsbF9tb25fY2VsbHMsIGRpbXMgPSAxOjI1LCB2ZXJib3NlID0gRikKRGltUGxvdChhbGxfbW9uX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIpCkRpbVBsb3QoYWxsX21vbl9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJhbGxjZWxsc19jbHVzdGVycyIpCkRpbVBsb3QoYWxsX21vbl9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJwY2EyNV9yZXMuMC42IikKRGltUGxvdChhbGxfbW9uX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIkRvbm9yIikKRGltUGxvdChhbGxfbW9uX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIlBoYXNlIikKRmVhdHVyZVBsb3QoYWxsX21vbl9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlcyA9IGMoIk1LSTY3IiwgIkFMQiIsICJTMTAwQTgiLCAiQ09MRUMxMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOS0c3IiwgIlRSR0MxIiwgIlBUUFJDIiwgIkVQQ0FNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMTQiLCAiVFJBQyIsICJDRDNFIiwgIkNEOEEiKSkKYGBgCgpDbHVzdGVyaW5nIGFuZCBtYXJrZXJzCgpgYGB7cn0KYWxsX21vbl9jZWxscyA9IEZpbmROZWlnaGJvcnMoYWxsX21vbl9jZWxscywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmUuU05OID0gMS81LCBmb3JjZS5yZWNhbGMgPSBULCBncmFwaC5uYW1lID0gInBjYTI1IikKYWxsX21vbl9jZWxscyA9IEZpbmRDbHVzdGVycyhhbGxfbW9uX2NlbGxzLCBhbGdvcml0aG0gPSAyLCB2ZXJib3NlID0gRiwgZ3JhcGgubmFtZSA9ICJwY2EyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBzZXEoMC4xLCAyLCAwLjEpKQpEaW1QbG90KGFsbF9tb25fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAicGNhMjVfcmVzLjAuNSIsIGxhYmVsID0gVCkKRGltUGxvdChhbGxfbW9uX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImFsbGNlbGxzX2NsdXN0ZXJzIiwgbGFiZWwgPSBUKQpEaW1QbG90KGFsbF9tb25fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiRG9ub3IiLCBsYWJlbCA9IEYpCkRpbVBsb3QoYWxsX21vbl9jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJDb25kaXRpb24iLCBsYWJlbCA9IEYpCgphbGxfbW9uX2NlbGxzID0gU2V0SWRlbnQoYWxsX21vbl9jZWxscywgdmFsdWUgPSAicGNhMjVfcmVzLjAuNSIpCm1rX21vbiA9IEZpbmRBbGxNYXJrZXJzKGFsbF9tb25fY2VsbHMsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgcHNldWRvY291bnQudXNlID0gMC4xKQp3cml0ZS5jc3YobWtfbW9uW21rX21vbiRwX3ZhbF9hZGo8PTAuMDUsXSwgCiAgICAgICAgICBmaWxlID0gInJlc3VsdHMvaW1tdW5lL21hcmtlcnNfbW9uX3N1YnBvcF9hbGwuY3N2Iiwgcm93Lm5hbWVzID0gVCwgcXVvdGUgPSBGKQoKc2F2ZVJEUyhta19tb24sIGZpbGUgPSAiLi9yZXN1bHRzL2ltbXVuZS9jbHVzdF9tYXJrZXJzX21vbi5SRFMiKQoKbWs2MSA9IEZpbmRNYXJrZXJzKGFsbF9tb25fY2VsbHMsIGlkZW50LjEgPSAiNiIsIGlkZW50LjIgPSAiMSIsIAogICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yLCBwc2V1ZG9jb3VudC51c2UgPSAwLjEpCm1rMjEgPSBGaW5kTWFya2VycyhhbGxfbW9uX2NlbGxzLCBpZGVudC4xID0gIjIiLCBpZGVudC4yID0gIjEiLCAKICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgcHNldWRvY291bnQudXNlID0gMC4xKQpgYGAKCkFkZCBhbm5vdGF0aW9ucwoKYGBge3J9Cm5ld19tX2xhYnMgPSBjKCIwIiA9ICJLdXBmZmVyIGNlbGxzIiwKICAgICAgICAgICAgICAgIjEiID0gImNEQzIiLAogICAgICAgICAgICAgICAiMiIgPSAiTW9ub2N5dGVzIChJR1NGMjErIEdQUjM0KykiLCAjIHNpbWlsYXIgdG8gdGhvc2UgaWRlbnRpZmllZCBoZXJlIGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAyMC0yOTIyLTQgCiAgICAgICAgICAgICAgICIzIiA9ICJNYWNyb3BoYWdlcyAoSEVTNCspIiwgIyB2ZXJ5IHNpbWlsYXIgdG8gNSwgSEVTNCBpcyBvbmUgb2YgaXRzIG1vc3QgdW5pcXVlIG1hcmtlcnMKICAgICAgICAgICAgICAgIjQiID0gIkt1cGZmZXIgY2VsbHMgKFNVQ05SMSspIiwgIyBkaXNwcm92ZXMgaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DMTk4NjU3NS87IHRoZXNlIEtDIGFyZSBpbmZsYW1tYXRvcnkvYW50aXZpcmFsCiAgICAgICAgICAgICAgICI1IiA9ICJNYWNyb3BoYWdlcyIsCiAgICAgICAgICAgICAgICI2IiA9ICJNb25vY3l0ZXMgKFRSRU0yKyBDRDkrKSIsICMgaW4gcmFtYWNoYW5kcmFuIGV0IGFsLCBhc3NvYyB3aXRoIGZpYnJvdGljIHNjYXJzCiAgICAgICAgICAgICAgICMjIGluIHRoZWlyIHByb2plY3Rpb24sIGFsc28gY2xvc2UgdG8gS0MKICAgICAgICAgICAgICAgIjciID0gImNEQzIiLAogICAgICAgICAgICAgICAiOCIgPSAiY0RDMSIsCiAgICAgICAgICAgICAgICI5IiA9ICJNb25vY3l0ZXMgKHNlY3JldG9yeSkiLCAjIHJlbGF0ZWQgdG8gMiBhbmQgNgogICAgICAgICAgICAgICAiMTAiID0gImFjdGl2YXRlZCBEQ3MiKSAjIENEODAsIENEODYsIENDUjcKCmFsbF9tb25fY2VsbHMkbW9ub19hbm5vdCA9IG5ld19tX2xhYnNbYXMuY2hhcmFjdGVyKGFsbF9tb25fY2VsbHMkcGNhMjVfcmVzLjAuNSldCmBgYAoKCiMjIFB1dCBhbm5vdGF0aW9ucyBvbiBzaW5nbGUgaW1tdW5lIGNlbGwgb2JqZWN0Ck1ha2UgZGF0YWZyYW1lIHdpdGggbmV3IGFubm90YXRpb25zCgpgYGB7cn0KbmV3YW5ub3RfbCA9IGxpc3QobGRmID0gYWxsX2xfY2VsbHNAbWV0YS5kYXRhWyxjKCJhbGxjZWxsc19jbHVzdGVycyIsICJseW1waG9pZF9hbm5vdCIpXSwKICAgICAgICAgICAgICAgICAgdGRmID0gYWxsX3RfY2VsbHNAbWV0YS5kYXRhWyxjKCJhbGxjZWxsc19jbHVzdGVycyIsICJ0X2Fubm90IildLAogICAgICAgICAgICAgICAgICBtZGYgPSBhbGxfbV9jZWxsc0BtZXRhLmRhdGFbLGMoImFsbGNlbGxzX2NsdXN0ZXJzIiwgIm15ZV9hbm5vdCIpXSwKICAgICAgICAgICAgICAgICAgbW9uZGYgPSBhbGxfbW9uX2NlbGxzQG1ldGEuZGF0YVssYygiYWxsY2VsbHNfY2x1c3RlcnMiLCAibW9ub19hbm5vdCIpXSkKZm9yKG4gaW4gbmFtZXMobmV3YW5ub3RfbCkpewogIG5ld2Fubm90X2xbW25dXSRjZWxscyA9IHJvd25hbWVzKG5ld2Fubm90X2xbW25dXSkKfQoKbmV3YW5ub3RfZGYgPSBSZWR1Y2UoZnVuY3Rpb24oeCx5KXttZXJnZSh4LHksIGJ5ID0gImNlbGxzIiwgYWxsID0gVCl9LCBuZXdhbm5vdF9sKVssYygxLDMsNSw3LDkpXQoKbmV3YW5ub3RfZGYkdF9hbm5vdFtpcy5uYShuZXdhbm5vdF9kZiR0X2Fubm90KV0gPSBuZXdhbm5vdF9kZiRseW1waG9pZF9hbm5vdFtpcy5uYShuZXdhbm5vdF9kZiR0X2Fubm90KV0KbmV3YW5ub3RfZGYkbW9ub19hbm5vdFtpcy5uYShuZXdhbm5vdF9kZiRtb25vX2Fubm90KV0gPSBuZXdhbm5vdF9kZiRteWVfYW5ub3RbaXMubmEobmV3YW5ub3RfZGYkbW9ub19hbm5vdCldCm5ld2Fubm90X2RmJGltbXVuZV9hbm5vdCA9IG5ld2Fubm90X2RmJHRfYW5ub3QKbmV3YW5ub3RfZGYkaW1tdW5lX2Fubm90W2lzLm5hKG5ld2Fubm90X2RmJGltbXVuZV9hbm5vdCldID0gbmV3YW5ub3RfZGYkbW9ub19hbm5vdFtpcy5uYShuZXdhbm5vdF9kZiRpbW11bmVfYW5ub3QpXQoKbmV3YW5ub3RfZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IG5ld2Fubm90X2RmJGNlbGxzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGltbXVuZV9hbm5vdCA9IG5ld2Fubm90X2RmJGltbXVuZV9hbm5vdCkKYGBgCgpBZGQgdG8gdGhlIGltbXVuZSBTZXVyYXQgb2JqZWN0CgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQphbGxfaW1tX2NlbGxzID0gQWRkTWV0YURhdGEoYWxsX2ltbV9jZWxscywgbWV0YWRhdGEgPSBuZXdhbm5vdF9kZikKIyB0aGUgb3JpZ2luYWwgIkRpdmlkaW5nIGNlbGxzIiB3aWxsIGJlIHJlbGFiZWxlZCAiRGl2aWRpbmcgVC9OSyBjZWxscyIsCiMjIGFuZCB0aGUgbmV3bHkgYW5ub3RhdGVkICJEaXZpZGluZyBOSyBjZWxscyIgd2lsbCBiZSByZW5hbWVkIHRvIG1hdGNoIHRoaXMKYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3RbaXMubmEoYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3QpXSA9ICJEaXZpZGluZyBUL05LIGNlbGxzIgphbGxfaW1tX2NlbGxzJGltbXVuZV9hbm5vdFthbGxfaW1tX2NlbGxzJGltbXVuZV9hbm5vdD09IkRpdmlkaW5nIE5LIGNlbGxzIl0gPSAiRGl2aWRpbmcgVC9OSyBjZWxscyIKYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3RbYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3Q9PSJIZXBhdG9jeXRlcyJdID0gIkhlcGF0b2N5dGUtTW9ub2N5dGUgaW50ZXJhY3Rpb24iCgpEaW1QbG90KGFsbF9sX2NlbGxzLCBncm91cC5ieSA9ICJseW1waG9pZF9hbm5vdCIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUKStOb0xlZ2VuZCgpK2dndGl0bGUoIkx5bXBob2lkIGNlbGxzIikKRGltUGxvdChhbGxfdF9jZWxscywgZ3JvdXAuYnkgPSAidF9hbm5vdCIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUKStOb0xlZ2VuZCgpK2dndGl0bGUoIlQgYW5kIE5LIGNlbGxzIChzdWJzZXQgb2YgTHltcGhvaWQpIikKRGltUGxvdChhbGxfbV9jZWxscywgZ3JvdXAuYnkgPSAibXllX2Fubm90IiwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFQpK05vTGVnZW5kKCkrZ2d0aXRsZSgiTXllbG9pZCBjZWxscyIpCkRpbVBsb3QoYWxsX21vbl9jZWxscywgZ3JvdXAuYnkgPSAibW9ub19hbm5vdCIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUKStOb0xlZ2VuZCgpK2dndGl0bGUoIk1vbm9jeXRlcyBjZWxscyAoc3Vic2V0IG9mIE15ZWxvaWQpIikKRGltUGxvdChhbGxfaW1tX2NlbGxzLCBncm91cC5ieSA9ICJhbGxjZWxsc19jbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUKStOb0xlZ2VuZCgpK2dndGl0bGUoIkltbXVuZSBjZWxscyAob3JpZ2luYWwgYW5ub3RhdGlvbikiKQpEaW1QbG90KGFsbF9pbW1fY2VsbHMsIGdyb3VwLmJ5ID0gImltbXVuZV9hbm5vdCIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUKStOb0xlZ2VuZCgpK2dndGl0bGUoIkltbXVuZSBjZWxscyAobmV3IGRldGFpbGVkIGFubm90YXRpb24pIikKYGBgCgpTYXZlIFNldXJhdCBvYmplY3RzCgpgYGB7cn0Kc2F2ZVJEUyhhbGxfbF9jZWxscywgZmlsZSA9ICJyZXN1bHRzL2ltbXVuZS9hbGxfbF9jZWxscy5SRFMiKQpzYXZlUkRTKGFsbF90X2NlbGxzLCBmaWxlID0gInJlc3VsdHMvaW1tdW5lL2FsbF90X2NlbGxzLlJEUyIpCnNhdmVSRFMoYWxsX21fY2VsbHMsIGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX21fY2VsbHMuUkRTIikKc2F2ZVJEUyhhbGxfbW9uX2NlbGxzLCBmaWxlID0gInJlc3VsdHMvaW1tdW5lL2FsbF9tb25fY2VsbHMuUkRTIikKc2F2ZVJEUyhhbGxfaW1tX2NlbGxzLCBmaWxlID0gInJlc3VsdHMvaW1tdW5lL2FsbF9pbW1fY2VsbHMuUkRTIikKYGBgCgoKCiMgQW5hbHlzaXMgb2YgaW1tdW5lIGNlbGwgdHlwZXMKTG9hZCBkYXRhCgpgYGB7cn0KYWxsX2xfY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX2xfY2VsbHMuUkRTIikKYWxsX3RfY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX3RfY2VsbHMuUkRTIikKYWxsX21fY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX21fY2VsbHMuUkRTIikKYWxsX21vbl9jZWxscyA9IHJlYWRSRFMoZmlsZSA9ICJyZXN1bHRzL2ltbXVuZS9hbGxfbW9uX2NlbGxzLlJEUyIpCmFsbF9pbW1fY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX2ltbV9jZWxscy5SRFMiKQpgYGAKCk1hcmtlcnMKCmBgYHtyfQpta19sY2VsbHMgPSByZWFkLmNzdigicmVzdWx0cy9pbW11bmUvbWFya2Vyc19seW1waG9pZF9zdWJwb3BfYWxsLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCBoZWFkZXIgPSBUKQpuZXdfbF9sYWJzID0gYygiMCIgPSAiYWItVCBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjEiID0gIk5LL2dkLVQgY2VsbHMiLAogICAgICAgICAgICAgICAiMiIgPSAiYWItVCBjZWxscyAyIiwKICAgICAgICAgICAgICAgIjMiID0gIkluZmlsdHJhdGluZyBOSyBjZWxscyIsICMgUFRHRFMsQ1gzQ1IxIChpbmZpbHQpCiAgICAgICAgICAgICAgICI0IiA9ICJJZ0ErIFBsYXNtYSBjZWxscyIsCiAgICAgICAgICAgICAgICI1IiA9ICJCIGNlbGxzIiwKICAgICAgICAgICAgICAgIjYiID0gIklnRysgUGxhc21hIGNlbGxzIiwKICAgICAgICAgICAgICAgIjciID0gIkRpdmlkaW5nIE5LIGNlbGxzIiwKICAgICAgICAgICAgICAgIjgiID0gImFiLVQgY2VsbHMgKHN0cmVzcykiKQpta19sY2VsbHMkYW5ub3QgPSBuZXdfbF9sYWJzW2FzLmNoYXJhY3Rlcihta19sY2VsbHMkY2x1c3RlcildCgpta190Y2VsbHMgPSByZWFkLmNzdigicmVzdWx0cy9pbW11bmUvbWFya2Vyc190X3N1YnBvcF9hbGwuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIGhlYWRlciA9IFQpCm5ld190X2xhYnMgPSBjKCIwIiA9ICJOSyBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjEiID0gIlRSTSBjZWxscyIsICMgbWF5IGhhdmUgQ0Q0IGFuZCBDRDg7IElUR0ExCiAgICAgICAgICAgICAgICIyIiA9ICJNQUlUIGNlbGxzIDIiLCAjIENYQ1I2LCBDQ1I2LCBDQ1I1LCBzb21lIFJPUkMgKG5vdCBERSB3aXRoIHRoZSBJTEMzKSwgc29tZSBaQlRCMTYKICAgICAgICAgICAgICAgIjMiID0gIk5LIGNlbGxzIDIiLAogICAgICAgICAgICAgICAiNCIgPSAiTksgY2VsbHMgMyIsCiAgICAgICAgICAgICAgICI1IiA9ICJDRDggYWItVCBjZWxscyAxIiwKICAgICAgICAgICAgICAgIjYiID0gIk1BSVQgY2VsbHMgMSIsICMgaGFyZCB0byBiZSBzdXJlLCBidXQgaGFzIG1hbnkgaGFsbG1hcmtzLCBldmVuIGhpZ2hlciBDRDhBCiAgICAgICAgICAgICAgICI3IiA9ICJJbmZpbHRyYXRpbmcgTksgY2VsbHMiLAogICAgICAgICAgICAgICAiOCIgPSAiTmFpdmUgQ0Q0KyBUIGNlbGxzIiwgCiAgICAgICAgICAgICAgICI5IiA9ICJnZC1UIGNlbGxzIiwgIyBDRDMrIHZzIGNsNywgVFJEQy9UUkcKICAgICAgICAgICAgICAgIjEwIiA9ICJDRDggYWItVCBjZWxscyAyIiwKICAgICAgICAgICAgICAgIjExIiA9ICJDRDggYWItVCBjZWxscyAoc3RyZXNzKSIsCiAgICAgICAgICAgICAgICIxMiIgPSAiVHJlZyIsCiAgICAgICAgICAgICAgICIxMyIgPSAiSUxDMyIpICMgS0lULCBBSFIsIFJPUkMsIG5vIENEMy9DRDgvQ0Q0Cm1rX3RjZWxscyRhbm5vdCA9IG5ld190X2xhYnNbYXMuY2hhcmFjdGVyKG1rX3RjZWxscyRjbHVzdGVyKV0KCm1rX21jZWxscyA9IHJlYWQuY3N2KCJyZXN1bHRzL2ltbXVuZS9tYXJrZXJzX21fc3VicG9wX2FsbC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwgaGVhZGVyID0gVCkKbmV3X21fbGFicyA9IGMoIjAiID0gIkt1cGZmZXIgY2VsbHMiLAogICAgICAgICAgICAgICAiMSIgPSAiTW9ub2N5dGVzL2NEQ3MiLAogICAgICAgICAgICAgICAiMiIgPSAiTWFjcm9waGFnZXMiLAogICAgICAgICAgICAgICAiMyIgPSAiTW9ub2N5dGVzL2NEQ3MiLAogICAgICAgICAgICAgICAiNCIgPSAiS3VwZmZlciBjZWxscyIsCiAgICAgICAgICAgICAgICI1IiA9ICJNb25vY3l0ZXMvY0RDcyIsCiAgICAgICAgICAgICAgICI2IiA9ICJNb25vY3l0ZXMvY0RDcyIsCiAgICAgICAgICAgICAgICI3IiA9ICJNYWNyb3BoYWdlcyIsCiAgICAgICAgICAgICAgICI4IiA9ICJNb25vY3l0ZXMvY0RDcyIsCiAgICAgICAgICAgICAgICI5IiA9ICJjREMxIiwKICAgICAgICAgICAgICAgIjEwIiA9ICJwRENzIiwKICAgICAgICAgICAgICAgIjExIiA9ICJwRENzIiwKICAgICAgICAgICAgICAgIjEyIiA9ICJEaXZpZGluZyBjRENzIiwKICAgICAgICAgICAgICAgIjEzIiA9ICJLdXBmZmVyIGNlbGxzIiwKICAgICAgICAgICAgICAgIjE0IiA9ICJIZXBhdG9jeXRlcyIpCm1rX21jZWxscyRhbm5vdCA9IG5ld19tX2xhYnNbYXMuY2hhcmFjdGVyKG1rX21jZWxscyRjbHVzdGVyKV0KCm1rX21vbm9jZWxscyA9IHJlYWQuY3N2KCJyZXN1bHRzL2ltbXVuZS9tYXJrZXJzX21vbl9zdWJwb3BfYWxsLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCBoZWFkZXIgPSBUKQpuZXdfbV9sYWJzID0gYygiMCIgPSAiS3VwZmZlciBjZWxscyIsCiAgICAgICAgICAgICAgICIxIiA9ICJjREMyIiwKICAgICAgICAgICAgICAgIjIiID0gIk1vbm9jeXRlcyAoSUdTRjIxKyBHUFIzNCspIiwgIyBzaW1pbGFyIHRvIHRob3NlIGlkZW50aWZpZWQgaGVyZSBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4Ni0wMjAtMjkyMi00IAogICAgICAgICAgICAgICAiMyIgPSAiTWFjcm9waGFnZXMgKEhFUzQrKSIsICMgdmVyeSBzaW1pbGFyIHRvIDUsIEhFUzQgaXMgb25lIG9mIGl0cyBtb3N0IHVuaXF1ZSBtYXJrZXJzCiAgICAgICAgICAgICAgICI0IiA9ICJLdXBmZmVyIGNlbGxzIChTVUNOUjErKSIsICMgZGlzcHJvdmVzIGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzE5ODY1NzUvOyB0aGVzZSBLQyBhcmUgaW5mbGFtbWF0b3J5L2FudGl2aXJhbAogICAgICAgICAgICAgICAiNSIgPSAiTWFjcm9waGFnZXMiLAogICAgICAgICAgICAgICAiNiIgPSAiTW9ub2N5dGVzIChUUkVNMisgQ0Q5KykiLCAjIGluIHJhbWFjaGFuZHJhbiBldCBhbCwgYXNzb2Mgd2l0aCBmaWJyb3RpYyBzY2FycwogICAgICAgICAgICAgICAjIyBpbiB0aGVpciBwcm9qZWN0aW9uLCBhbHNvIGNsb3NlIHRvIEtDCiAgICAgICAgICAgICAgICI3IiA9ICJjREMyIiwKICAgICAgICAgICAgICAgIjgiID0gImNEQzEiLAogICAgICAgICAgICAgICAiOSIgPSAiTW9ub2N5dGVzIChzZWNyZXRvcnkpIiwgIyByZWxhdGVkIHRvIDIgYW5kIDYKICAgICAgICAgICAgICAgIjEwIiA9ICJhY3RpdmF0ZWQgRENzIikgIyBDRDgwLCBDRDg2LCBDQ1I3Cm1rX21vbm9jZWxscyRhbm5vdCA9IG5ld19tX2xhYnNbYXMuY2hhcmFjdGVyKG1rX21vbm9jZWxscyRjbHVzdGVyKV0KYGBgCgpQbG90IFVNQVBzCgpgYGB7cn0KaW1tX2RmID0gZGF0YS5mcmFtZShFbWJlZGRpbmdzKGFsbF9pbW1fY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIikpCmltbV9kZiRhbGxfYW5ub3QgPSBhbGxfaW1tX2NlbGxzJGltbXVuZV9hbm5vdAppbW1fZGYkc2ltcF9hbm5vdCA9IGltbV9kZiRhbGxfYW5ub3QKaW1tX2RmJHNpbXBfYW5ub3RbaW1tX2RmJHNpbXBfYW5ub3QgJWluJSBjKCJhYi1UIGNlbGxzIChzdHJlc3MpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGVwYXRvY3l0ZS1Nb25vY3l0ZSBpbnRlcmFjdGlvbiIpXSA9ICJOb24gYW5ub3RhdGVkIGNlbGxzIgptb25vID0gdW5pcXVlKGltbV9kZiRhbGxfYW5ub3QpW2MoMiw0OjksMTE6MTIsMzEpXQppbW1fZGYkc2ltcF9hbm5vdFtpbW1fZGYkc2ltcF9hbm5vdCAlaW4lIG1vbm9dID0gIk1vbm9jeXRlcy9NYWNyb3BoYWdlcy9jRENzIgp0bmsgPSB1bmlxdWUoaW1tX2RmJGFsbF9hbm5vdClbYygxNzoyMCwyMjozMCwzMildCmltbV9kZiRzaW1wX2Fubm90W2ltbV9kZiRzaW1wX2Fubm90ICVpbiUgdG5rXSA9ICJUIGNlbGxzL0lMQ3MiCgpjb2xzID0gTWV0QnJld2VyOjptZXQuYnJld2VyKCJFZ3lwdCIsIGxlbmd0aCh1bmlxdWUoaW1tX2RmJHNpbXBfYW5ub3QpKSkKbmFtZXMoY29scykgPSB1bmlxdWUoaW1tX2RmJHNpbXBfYW5ub3QpCmNvbHNbIk5vbiBhbm5vdGF0ZWQgY2VsbHMiXSA9ICJncmV5ODAiCgppbW1fZGYgPSBpbW1fZGZbc2FtcGxlKDE6bnJvdyhpbW1fZGYpLCBucm93KGltbV9kZiksIHJlcGxhY2UgPSBGKSxdCmdncGxvdCgpKwogIGdlb21fcG9pbnQoYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG91ciA9IHNpbXBfYW5ub3QpLCAKICAgICAgICAgICAgIGltbV9kZiwgc2l6ZSA9IDAuMzUpKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbHNbb3JkZXIobmFtZXMoY29scykpXSkrCiAgdGhlbWVfdm9pZCgpKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKCmBgYHtyfQp0X2RmID0gZGF0YS5mcmFtZShFbWJlZGRpbmdzKGFsbF90X2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIpKQp0X2RmJGFsbF9hbm5vdCA9IGFsbF90X2NlbGxzJHRfYW5ub3QKCmNvbHMgPSBNZXRCcmV3ZXI6Om1ldC5icmV3ZXIoIkp1YXJleiIsIGxlbmd0aCh1bmlxdWUodF9kZiRhbGxfYW5ub3QpKSkKbmFtZXMoY29scykgPSB1bmlxdWUodF9kZiRhbGxfYW5ub3QpCgp0X2RmID0gdF9kZltzYW1wbGUoMTpucm93KHRfZGYpLCBucm93KHRfZGYpLCByZXBsYWNlID0gRiksXQpnZ3Bsb3QoKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvdXIgPSBhbGxfYW5ub3QpLCAKICAgICAgICAgICAgIHRfZGYsIHNpemUgPSAwLjM1KSsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkrCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xzW29yZGVyKG5hbWVzKGNvbHMpKV0pKwogIHRoZW1lX3ZvaWQoKSsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKCgpgYGB7cn0KbV9kZiA9IGRhdGEuZnJhbWUoRW1iZWRkaW5ncyhhbGxfbW9uX2NlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIpKQptX2RmJGFsbF9hbm5vdCA9IGFsbF9tb25fY2VsbHMkbW9ub19hbm5vdAoKY29scyA9IE1ldEJyZXdlcjo6bWV0LmJyZXdlcigiS2xpbXQiLCBsZW5ndGgodW5pcXVlKG1fZGYkYWxsX2Fubm90KSkpCm5hbWVzKGNvbHMpID0gdW5pcXVlKG1fZGYkYWxsX2Fubm90KQoKbV9kZiA9IG1fZGZbc2FtcGxlKDE6bnJvdyhtX2RmKSwgbnJvdyhtX2RmKSwgcmVwbGFjZSA9IEYpLF0KZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3VyID0gYWxsX2Fubm90KSwgCiAgICAgICAgICAgICBtX2RmLCBzaXplID0gMC4zNSkrCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sc1tvcmRlcihuYW1lcyhjb2xzKSldKSsKICB0aGVtZV92b2lkKCkrCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClNhdmUgZGF0YSBmb3IgVU1BUHMKCmBgYHtyfQpzYXZlUkRTKG1fZGYsIGZpbGUgPSAicmVzdWx0cy9jaXJyaG9zaXMvdW1hcF9tX2RmLlJEUyIpCnNhdmVSRFModF9kZiwgZmlsZSA9ICJyZXN1bHRzL2NpcnJob3Npcy91bWFwX3RfZGYuUkRTIikKc2F2ZVJEUyhpbW1fZGYsIGZpbGUgPSAicmVzdWx0cy9jaXJyaG9zaXMvdW1hcF9pbW1fZGYuUkRTIikKYGBgCgpQcmVwYXJlIG1hcmtlciBoZWF0bWFwcwoKYGBge3J9CnRvcG1rX2wgPSBta19sY2VsbHMgJT4lCiAgZ3JvdXBfYnkoYW5ub3QpICU+JQogIHRvcF9uKG4gPSAxMDAsIHd0ID0gYXZnX2xvZzJGQykKdG9wbWtfdCA9IG1rX3RjZWxscyAlPiUKICBncm91cF9ieShhbm5vdCkgJT4lCiAgdG9wX24obiA9IDEwMCwgd3QgPSBhdmdfbG9nMkZDKQp0b3Bta19tID0gbWtfbWNlbGxzJT4lCiAgZ3JvdXBfYnkoYW5ub3QpICU+JQogIHRvcF9uKG4gPSAxMDAsIHd0ID0gYXZnX2xvZzJGQykKdG9wbWtfbW9ubyA9IG1rX21vbm9jZWxscyU+JQogIGdyb3VwX2J5KGFubm90KSAlPiUKICB0b3BfbihuID0gMTAwLCB3dCA9IGF2Z19sb2cyRkMpCgpta191c2UgPSBjKCJQVEdEUyIsIkNYM0NSMSIsICJLSVQiLCAiQUhSIiwgIlJPUkMiLCAiQ0QzRSIsICJDQ1I3IiwgIk1LSTY3IiwgIkNENDQiLCAiSUw3UiIsCiAgICAgICAgICAgIkNEODAiLCAiQ0Q4NiIsICJUUkVNMiIsICJDRDkiLCAiSEVTNCIsICJJR1NGMjEiLCAiR1BSMzQiLCAiSUdIRzEiLCAiQ0Q4QSIsICJDRDVMIiwKICAgICAgICAgICAiQ0Q0IiwgIkZPWFAzIiwgIkNUTEE0IiwgIkNENzlBIiwgIklHSEExIiwgIklHSE0iLCAiQ1hDTDEwIiwgIkxZVkUxIiwgIk1BUkNPIiwKICAgICAgICAgICAiUzEwMEE4IiwiTFlaIiwiU0VMTCIsIklGSTI3IiwiRkNFUjFBIiwgIklUR0ExIiwgIlRIRU1JUyIsIlMxMDBCIiwKICAgICAgICAgICAiVFJBQyIsICJUUkJDMSIsICJUUkRDIiwgIlRSR0MxIiwgIktMUkMyIiwgIkNDTDE5IiwgCiAgICAgICAgICAgIklETzEiLCAiQ0xFQzlBIiwgIklMMlJCIiwgIkNYQ1I2IiwgIkNDUjYiLCAiQ0NSNSIsICJST1JDIiwgIlpCVEIxNiIpCgpta19tID0gbWtfdXNlW21rX3VzZSAlaW4lIHRvcG1rX21vbm8kZ2VuZSB8IG1rX3VzZSAlaW4lIHRvcG1rX20kZ2VuZV0KbWtfbCA9IG1rX3VzZVtta191c2UgJWluJSB0b3Bta19sJGdlbmUgfCBta191c2UgJWluJSB0b3Bta190JGdlbmVdCgp0b3Bta19sID0gbWtfbGNlbGxzICU+JQogIGdyb3VwX2J5KGFubm90KSAlPiUKICB0b3BfbihuID0gMywgd3QgPSBhdmdfbG9nMkZDKQp0b3Bta19sX2wgPSB0YXBwbHkodG9wbWtfbCRnZW5lLCB0b3Bta19sJGFubm90LCBmdW5jdGlvbih4KSB4WzE6M10pCnRvcG1rX3QgPSBta190Y2VsbHMgJT4lCiAgZ3JvdXBfYnkoYW5ub3QpICU+JQogIHRvcF9uKG4gPSAzLCB3dCA9IGF2Z19sb2cyRkMpCnRvcG1rX3RfbCA9IHRhcHBseSh0b3Bta190JGdlbmUsIHRvcG1rX3QkYW5ub3QsIGZ1bmN0aW9uKHgpIHhbMTozXSkKdG9wbWtfbSA9IG1rX21jZWxscyU+JQogIGdyb3VwX2J5KGFubm90KSAlPiUKICB0b3BfbihuID0gMywgd3QgPSBhdmdfbG9nMkZDKQp0b3Bta19tX2wgPSB0YXBwbHkodG9wbWtfbSRnZW5lLCB0b3Bta19tJGFubm90LCBmdW5jdGlvbih4KSB4WzE6M10pCnRvcG1rX21vbm8gPSBta19tb25vY2VsbHMlPiUKICBncm91cF9ieShhbm5vdCkgJT4lCiAgdG9wX24obiA9IDMsIHd0ID0gYXZnX2xvZzJGQykKdG9wbWtfbW9ub19sID0gdGFwcGx5KHRvcG1rX21vbm8kZ2VuZSwgdG9wbWtfbW9ubyRhbm5vdCwgZnVuY3Rpb24oeCkgeFsxOjNdKQoKcmVtX2N0ID0gYygiYWItVCBjZWxscyAoc3RyZXNzKSIsICJDRDggYWItVCBjZWxscyAoc3RyZXNzKSIpCmlkX20gPSB1bmlxdWUobmFtZXMoYyh0b3Bta19tX2wsIHRvcG1rX21vbm9fbCkpKQppZF9tID0gaWRfbVtpZF9tICVpbiUgYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3QgJiAhaWRfbSAlaW4lIHJlbV9jdF0KZmVhdF9tID0gdW5pcXVlKGModW5saXN0KGModG9wbWtfbV9sLCB0b3Bta19tb25vX2wpW2lkX21dKSwgbWtfbSkpCmZlYXRfbSA9IGZlYXRfbVshZ3JlcGwoIi4iLGZlYXRfbSxmaXhlZCA9IFQpXQoKaWRfbCA9IHVuaXF1ZShuYW1lcyhjKHRvcG1rX2xfbCwgdG9wbWtfdF9sKSkpCmlkX2wgPSBpZF9sW2lkX2wgJWluJSBhbGxfaW1tX2NlbGxzJGltbXVuZV9hbm5vdCAmICFpZF9sICVpbiUgcmVtX2N0XQpmZWF0X2wgPSB1bmlxdWUoYyh1bmxpc3QoYyh0b3Bta19sX2wsIHRvcG1rX3RfbClbaWRfbF0pLCBta19sKSkKZmVhdF9sID0gZmVhdF9sWyFncmVwbCgiLiIsZmVhdF9sLGZpeGVkID0gVCldCgphdmdfbSA9IEF2ZXJhZ2VFeHByZXNzaW9uKGFsbF9pbW1fY2VsbHMsIGZlYXR1cmVzID0gZmVhdF9tLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJpbW11bmVfYW5ub3QiKSRTQ1RbLGlkX21dCmF2Z19sID0gQXZlcmFnZUV4cHJlc3Npb24oYWxsX2ltbV9jZWxscywgZmVhdHVyZXMgPSBmZWF0X2wsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImltbXVuZV9hbm5vdCIpJFNDVFssaWRfbF0KCm1hdF9tID0gc2NhbGUodChhdmdfbSkpCm1hdF9sID0gc2NhbGUodChhdmdfbCkpCgpjb2xzX3BhbCA9IGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwobiA9IDksIG5hbWUgPSAiUmRCdSIpKSkoMTAwKQpwaGVhdG1hcDo6cGhlYXRtYXAobWF0X20sIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRCIsIGNvbG9yID0gY29sc19wYWwsCiAgICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X2NvbCA9IDAsIHRyZWVoZWlnaHRfcm93ID0gMjAsIGZvbnRzaXplID0gNi41KQpwaGVhdG1hcDo6cGhlYXRtYXAobWF0X2wsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRCIsIGNvbG9yID0gY29sc19wYWwsCiAgICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X2NvbCA9IDAsIHRyZWVoZWlnaHRfcm93ID0gMjAsIGZvbnRzaXplID0gNi41KQpgYGAKClNhdmUgbWF0cmljZXMgZm9yIG15ZWxvaWQgYW5kIGx5bXBob2lkIG1hcmtlcnMKCmBgYHtyfQpzYXZlUkRTKG1hdF9tLCBmaWxlID0gInJlc3VsdHMvY2lycmhvc2lzL21hdF9teWVsb2lkX21hcmtlcnMuUkRTIikKc2F2ZVJEUyhtYXRfbCwgZmlsZSA9ICJyZXN1bHRzL2NpcnJob3Npcy9tYXRfbHltcGhvaWRfbWFya2Vycy5SRFMiKQpgYGAKCkNoYW5nZXMgaW4gcHJvcG9ydGlvbnMgYmV0d2VlbiBjb25kaXRpb25zCgpgYGB7cn0KZG9uX2NvbmRfZGYgPSB1bmlxdWUoYWxsX2ltbV9jZWxsc0BtZXRhLmRhdGFbLGMoIk5hbWUiLCAiRG9ub3IiLCAiQ29uZGl0aW9uIildKQp0YWJfY3RDb25kID0gdGFibGUoYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3QsIGFsbF9pbW1fY2VsbHMkTmFtZSkKdGFiX2N0Q29uZCA9IHRhYl9jdENvbmRbIWdyZXBsKCJzdHJlc3MiLCByb3duYW1lcyh0YWJfY3RDb25kKSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAhZ3JlcGwoImludGVyYSIsIHJvd25hbWVzKHRhYl9jdENvbmQpKSxdCmltbV9wcm9wcyA9IHQodCh0YWJfY3RDb25kKS9jb2xTdW1zKHRhYl9jdENvbmQpKSoxMDAKaW1tX3Byb3BzID0gbWVyZ2UoZGF0YS5mcmFtZShpbW1fcHJvcHMpLCBkb25fY29uZF9kZiwgYnkueCA9IDIsIGJ5LnkgPSAxKQoKZ2dwbG90KGltbV9wcm9wcywgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGcmVxLCBncm91cCA9IENvbmRpdGlvbiwgY29sb3VyID0gQ29uZGl0aW9uKSkrCiAgZmFjZXRfd3JhcCh+VmFyMSwgc2NhbGVzID0gImZyZWUiKSsKICBnZW9tX2ppdHRlcihwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKGppdHRlci53aWR0aCA9IDAuMywgZG9kZ2Uud2lkdGggPSAxKSkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjM1LCBjb2xvdXIgPSAiYmxhY2siKSsKICB0aGVtZV9idygpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgp0YWJfY3RDb25kID0gdGFibGUoYWxsX2ltbV9jZWxscyRpbW11bmVfYW5ub3QsIGFsbF9pbW1fY2VsbHMkTmFtZSkKdGFiX2N0Q29uZCA9IHRhYl9jdENvbmRbIWdyZXBsKCJzdHJlc3MiLCByb3duYW1lcyh0YWJfY3RDb25kKSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAhZ3JlcGwoImludGVyYSIsIHJvd25hbWVzKHRhYl9jdENvbmQpKSxdCmltbV9jbnRzID0gdGFiX2N0Q29uZAppbW1fY250cyA9IG1lcmdlKGRhdGEuZnJhbWUoaW1tX2NudHMpLCBkb25fY29uZF9kZiwgYnkueCA9IDIsIGJ5LnkgPSAxKQpwdmFsc19sID0gbGlzdCgpCmZvcihjdCBpbiB1bmlxdWUoaW1tX2NudHMkVmFyMSkpewogIGRmY3QgPSBpbW1fY250c1tpbW1fY250cyRWYXIxPT1jdCxdCiAgZGZjdCRub3QgPSB0YXBwbHkoaW1tX2NudHMkRnJlcSwgaW1tX2NudHMkVmFyMiwgc3VtKS1kZmN0JEZyZXEKICBkZmN0JHRvdCA9IHRhcHBseShpbW1fY250cyRGcmVxLCBpbW1fY250cyRWYXIyLCBzdW0pCiAgZGZjdCRDb25kaXRpb24gPSBmYWN0b3IoZGZjdCRDb25kaXRpb24sIGxldmVscyA9IGMoImhlYWx0aHkiLCAiZW1ib2xpc2VkIiwgInJlZ2VuZXJhdGluZyIpKQogIG1vZCA9IGdsbShjYmluZChGcmVxLCBub3QpIH4gQ29uZGl0aW9uICsgRG9ub3IgKyB0b3QsIGRhdGEgPSBkZmN0LCBmYW1pbHkgPSAiYmlub21pYWwiKQogIHB2YWxzX2xbW2N0XV0gPSBzdW1tYXJ5KG1vZCkkY29lZmZpY2llbnRzW2MoIkNvbmRpdGlvbmVtYm9saXNlZCIsICJDb25kaXRpb25yZWdlbmVyYXRpbmciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHIoPnx6fCkiXQp9CnB2YWxzX2RmID0gZGF0YS5mcmFtZSh0KGRhdGEuZnJhbWUocHZhbHNfbCkpKQpyb3duYW1lcyhwdmFsc19kZikgPSBuYW1lcyhwdmFsc19sKQpjb2xuYW1lcyhwdmFsc19kZikgPSBnc3ViKCJDb25kaXRpb24iLCAiIiwgY29sbmFtZXMocHZhbHNfZGYpKQpwdmFsc19kZiRlbWJvbGlzZWQgPSBwLmFkanVzdChwdmFsc19kZiRlbWJvbGlzZWQsIG1ldGhvZCA9ICJmZHIiKQpwdmFsc19kZiRyZWdlbmVyYXRpbmcgPSBwLmFkanVzdChwdmFsc19kZiRyZWdlbmVyYXRpbmcsIG1ldGhvZCA9ICJmZHIiKQpgYGAKClNhdmUgcHJvcG9ydGlvbnMgZGF0YQoKYGBge3J9CnNhdmVSRFMoaW1tX3Byb3BzLCBmaWxlID0gInJlc3VsdHMvY2lycmhvc2lzL2ltbV9wcm9wc19kYXQuUkRTIikKc2F2ZVJEUyhwdmFsc19kZiwgZmlsZSA9ICJyZXN1bHRzL2NpcnJob3Npcy9pbW1fcHJvcHNfcHZhbC5SRFMiKQpgYGAKCkxvYWQgUmFtYWNoYW5kcmFuIGRhdGEKCmBgYHtyfQpmID0gInJlc3VsdHMvY2lycmhvc2lzL2NpcnJfZGF0YV9yZW5vcm0uUkRTIgppZighZmlsZS5leGlzdHMoZikpewogIGxvYWQoIi4uLy4uL2RhdGEvcHVibGlzaGVkL1JhbWFjaGFuZHJhbl9saXZlci90aXNzdWUucmRhdGEiKQogIAogIGNpcnJfZGF0YSA9IENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSB0aXNzdWVAcmF3LmRhdGEsIG1ldGEuZGF0YSA9IHRpc3N1ZUBtZXRhLmRhdGEpCiAgCiAgIyBSZW5vcm1hbGlzZSwgc2luY2UgdGhlIG9yaWdpbmFsIGRhdGEgZG9lc24ndCBsb29rIG9rCiAgY2lycl9kYXRhID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShjaXJyX2RhdGEsIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzPWMoImRhdGFzZXQiLCAibkNvdW50X1JOQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSBOVUxMKSkKICAKICAKICBjaXJyX2RhdGEkYW5ub19jb25kID0gcGFzdGUwKGNpcnJfZGF0YSRhbm5vdGF0aW9uX2luZGVwdGgsICJfIiwgY2lycl9kYXRhJGNvbmRpdGlvbikKICBzYXZlUkRTKGNpcnJfZGF0YSwgZmlsZSA9IGYpCn0gZWxzZXsKICBjaXJyX2RhdGEgPSByZWFkUkRTKGYpCn0KYGBgCgpTdWJzZXQgUmFtYWNoYW5kcmFuIGFuZCBnZXQgSFZHCgpgYGB7cn0KZiA9ICJyZXN1bHRzL2NpcnJob3Npcy9jaXJyX2RhdGFfYWxsaW1tdW5lX3Jlbm9ybS5SRFMiCmlmKCFmaWxlLmV4aXN0cyhmKSl7CiAgY2lycl9pbW1fZGF0YSA9IGNpcnJfZGF0YVssY2lycl9kYXRhJGFubm90YXRpb25fbGluZWFnZSAlaW4lIGMoIk1QcyIsICJUY2VsbHMiLCAiSUxDcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJjZWxscyIsICJwRENzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBsYXNtYSBCY2VsbHMiLCAiTWFzdCBjZWxscyIpXQogIGNpcnJfaW1tX2RhdGEgPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNpcnJfaW1tX2RhdGEsIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzPWMoImRhdGFzZXQiLCAibkNvdW50X1JOQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCiAgc2F2ZVJEUyhjaXJyX2ltbV9kYXRhLCBmaWxlID0gInJlc3VsdHMvY2lycmhvc2lzL2NpcnJfZGF0YV9hbGxpbW11bmVfcmVub3JtLlJEUyIpCn0gZWxzZXsKICBjaXJyX2ltbV9kYXRhID0gcmVhZFJEUyhmKQp9CgojIE1QcyBvbmx5CmYgPSAicmVzdWx0cy9jaXJyaG9zaXMvY2lycl9kYXRhX01Qc19yZW5vcm0uUkRTIgppZighZmlsZS5leGlzdHMoZikpewogIGNpcnJfbXBzX2RhdGEgPSBjaXJyX2RhdGFbLGNpcnJfZGF0YSRhbm5vdGF0aW9uX2xpbmVhZ2UgJWluJSBjKCJNUHMiKV0KICBjaXJyX21wc19kYXRhID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShjaXJyX21wc19kYXRhLCBkby5jb3JyZWN0LnVtaSA9IFQsIHZlcmJvc2UgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJkYXRhc2V0IiwgIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIHNhdmVSRFMoY2lycl9tcHNfZGF0YSwgZmlsZSA9ICJyZXN1bHRzL2NpcnJob3Npcy9jaXJyX2RhdGFfTVBzX3Jlbm9ybS5SRFMiKQp9IGVsc2V7CiAgY2lycl9tcHNfZGF0YSA9IHJlYWRSRFMoZikKfQoKIyBUY2VsbHMgYW5kIElMQ3Mgb25seQpmID0gInJlc3VsdHMvY2lycmhvc2lzL2NpcnJfZGF0YV90Y2VsbHNfcmVub3JtLlJEUyIKaWYoIWZpbGUuZXhpc3RzKGYpKXsKICBjaXJyX3RjZV9kYXRhID0gY2lycl9kYXRhWyxjaXJyX2RhdGEkYW5ub3RhdGlvbl9saW5lYWdlICVpbiUgYygiVGNlbGxzIiwgIklMQ3MiKV0KICBjaXJyX3RjZV9kYXRhID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShjaXJyX3RjZV9kYXRhLCBkby5jb3JyZWN0LnVtaSA9IFQsIHZlcmJvc2UgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJkYXRhc2V0IiwgIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIHNhdmVSRFMoY2lycl90Y2VfZGF0YSwgZmlsZSA9IGYpCn0gZWxzZXsKICBjaXJyX3RjZV9kYXRhID0gcmVhZFJEUyhmKQp9CgpgYGAKCkNvcnJlbGF0aW9uIHdpdGggUmFtYWNoYW5kcmFuIGRhdGEKCmBgYHtyfQpjaXJfZGF0X2wgPSBsaXN0KCJhbGwiID0gY2lycl9pbW1fZGF0YSwgInRjZWxscyIgPSBjaXJyX3RjZV9kYXRhLCAibW9ubyIgPSBjaXJyX21wc19kYXRhKQppbW1fZGF0X2wgPSBsaXN0KCJhbGwiID0gYWxsX2ltbV9jZWxscywgCiAgICAgICAgICAgICAgICAgInRjZWxscyIgPSBhbGxfdF9jZWxsc1ssIWdyZXBsKCJzdHJlc3MiLGFsbF90X2NlbGxzJHRfYW5ub3QpXSwgCiAgICAgICAgICAgICAgICAgIm1vbm8iID0gYWxsX21vbl9jZWxscykKcGx0X2Nvcl9sID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGNpcl9kYXRfbCkpewogIGZlYXRfdXNlID0gaW50ZXJzZWN0KGNpcl9kYXRfbFtbbl1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgIGltbV9kYXRfbFtbbl1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKQogIAogIGxhYiA9IGlmZWxzZShuPT0iYWxsIiwgImltbXVuZV9hbm5vdCIsaWZlbHNlKG49PSJ0Y2VsbHMiLCAidF9hbm5vdCIsICJtb25vX2Fubm90IikpCiAgYXZnX2ltbSA9IEF2ZXJhZ2VFeHByZXNzaW9uKGltbV9kYXRfbFtbbl1dLCBmZWF0dXJlcyA9IGZlYXRfdXNlLCBncm91cC5ieSA9IGxhYikkU0NUCiAgYXZnX2NpciA9IEF2ZXJhZ2VFeHByZXNzaW9uKGNpcl9kYXRfbFtbbl1dLCBmZWF0dXJlcyA9IGZlYXRfdXNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3RhdGlvbl9pbmRlcHRoIikkU0NUCiAgCiAgIyBub3JtYWxpc2Ugcm93cwogIGF2Z19pbW0gPSB0KGFwcGx5KGF2Z19pbW0sIDEsIGZ1bmN0aW9uKHgpIHgvbWVhbih4KSkpCiAgYXZnX2NpciA9IHQoYXBwbHkoYXZnX2NpciwgMSwgZnVuY3Rpb24oeCkgeC9tZWFuKHgpKSkKICAKICBjb3JfZHMgPSBwc3ljaDo6Y29yci50ZXN0KGF2Z19pbW0sIGF2Z19jaXIsIG1ldGhvZCA9ICJzcCIpCiAgY29yX2RzJG1heHJvdyA9IGFwcGx5KGNvcl9kcyRyLCAxLCB3aGljaC5tYXgpCiAgY29yX2RzJG1heGNvbCA9IGFwcGx5KGNvcl9kcyRyLCAyLCB3aGljaC5tYXgpCiAgCiAgCiAgcGx0X2Nvcl9sW1tuXV0gPSBwbG90Q29ycihjb3JfZHMsICJUaGlzIHN0dWR5IiwgIlJhbWFjaGFuZHJhbiBldCBhbC4iKQp9CmBgYAoKCg==